mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-30 04:26:44 +00:00
Order table variants (#8295)
* BuildOrderTable: Show variants - Allow filtering of build orders by part variant * Add "include_variants" filter for SalesOrder table - A bit tricker! * Add "include_variants" filter to PartPurchaseOrdersTable * Enable filtering ReturnOrder by "part" attribute * Add similiar functionality for SalesOrderAllocation * Add similar filter for BuildAllocation table * Add migration file
This commit is contained in:
parent
0c9e986796
commit
59fa3bb4ff
@ -1,13 +1,19 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 268
|
INVENTREE_API_VERSION = 269
|
||||||
|
|
||||||
"""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 = """
|
||||||
|
|
||||||
|
v269 - 2024-10-16 : https://github.com/inventree/InvenTree/pull/8295
|
||||||
|
- Adds "include_variants" filter to the BuildOrder API endpoint
|
||||||
|
- Adds "include_variants" filter to the SalesOrder API endpoint
|
||||||
|
- Adds "include_variants" filter to the PurchaseOrderLineItem API endpoint
|
||||||
|
- Adds "include_variants" filter to the ReturnOrder API endpoint
|
||||||
|
|
||||||
268 - 2024-10-11 : https://github.com/inventree/InvenTree/pull/8274
|
268 - 2024-10-11 : https://github.com/inventree/InvenTree/pull/8274
|
||||||
- Adds "in_stock" attribute to the StockItem serializer
|
- Adds "in_stock" attribute to the StockItem serializer
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ class BuildFilter(rest_filters.FilterSet):
|
|||||||
model = Build
|
model = Build
|
||||||
fields = [
|
fields = [
|
||||||
'sales_order',
|
'sales_order',
|
||||||
'part',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
status = rest_filters.NumberFilter(label='Status')
|
status = rest_filters.NumberFilter(label='Status')
|
||||||
@ -54,6 +53,39 @@ class BuildFilter(rest_filters.FilterSet):
|
|||||||
field_name='parent',
|
field_name='parent',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
include_variants = rest_filters.BooleanFilter(label=_('Include Variants'), method='filter_include_variants')
|
||||||
|
|
||||||
|
def filter_include_variants(self, queryset, name, value):
|
||||||
|
"""Filter by whether or not to include variants of the selected part.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- This filter does nothing by itself, and requires the 'part' filter to be set.
|
||||||
|
- Refer to the 'filter_part' method for more information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
part = rest_filters.ModelChoiceFilter(
|
||||||
|
queryset=part.models.Part.objects.all(),
|
||||||
|
field_name='part',
|
||||||
|
method='filter_part'
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_part(self, queryset, name, part):
|
||||||
|
"""Filter by 'part' which is being built.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- If "include_variants" is True, include all variants of the selected part.
|
||||||
|
- Otherwise, just filter by the selected part.
|
||||||
|
"""
|
||||||
|
|
||||||
|
include_variants = str2bool(self.data.get('include_variants', False))
|
||||||
|
|
||||||
|
if include_variants:
|
||||||
|
return queryset.filter(part__in=part.get_descendants(include_self=True))
|
||||||
|
else:
|
||||||
|
return queryset.filter(part=part)
|
||||||
|
|
||||||
ancestor = rest_filters.ModelChoiceFilter(
|
ancestor = rest_filters.ModelChoiceFilter(
|
||||||
queryset=Build.objects.all(),
|
queryset=Build.objects.all(),
|
||||||
label=_('Ancestor Build'),
|
label=_('Ancestor Build'),
|
||||||
@ -581,13 +613,45 @@ class BuildItemFilter(rest_filters.FilterSet):
|
|||||||
'install_into',
|
'install_into',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
include_variants = rest_filters.BooleanFilter(
|
||||||
|
label=_('Include Variants'), method='filter_include_variants'
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_include_variants(self, queryset, name, value):
|
||||||
|
"""Filter by whether or not to include variants of the selected part.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- This filter does nothing by itself, and requires the 'part' filter to be set.
|
||||||
|
- Refer to the 'filter_part' method for more information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
part = rest_filters.ModelChoiceFilter(
|
part = rest_filters.ModelChoiceFilter(
|
||||||
queryset=part.models.Part.objects.all(),
|
queryset=part.models.Part.objects.all(),
|
||||||
|
label=_('Part'),
|
||||||
|
method='filter_part',
|
||||||
field_name='stock_item__part',
|
field_name='stock_item__part',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def filter_part(self, queryset, name, part):
|
||||||
|
"""Filter by 'part' which is being built.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- If "include_variants" is True, include all variants of the selected part.
|
||||||
|
- Otherwise, just filter by the selected part.
|
||||||
|
"""
|
||||||
|
|
||||||
|
include_variants = str2bool(self.data.get('include_variants', False))
|
||||||
|
|
||||||
|
if include_variants:
|
||||||
|
return queryset.filter(stock_item__part__in=part.get_descendants(include_self=True))
|
||||||
|
else:
|
||||||
|
return queryset.filter(stock_item__part=part)
|
||||||
|
|
||||||
build = rest_filters.ModelChoiceFilter(
|
build = rest_filters.ModelChoiceFilter(
|
||||||
queryset=build.models.Build.objects.all(),
|
queryset=build.models.Build.objects.all(),
|
||||||
|
label=_('Build Order'),
|
||||||
field_name='build_line__build',
|
field_name='build_line__build',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 4.2.16 on 2024-10-16 06:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('part', '0130_alter_parttesttemplate_part'),
|
||||||
|
('build', '0052_build_status_custom_key_alter_build_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='build',
|
||||||
|
name='part',
|
||||||
|
field=models.ForeignKey(help_text='Select part to build', limit_choices_to={'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.part', verbose_name='Part'),
|
||||||
|
),
|
||||||
|
]
|
@ -270,8 +270,6 @@ class Build(
|
|||||||
related_name='builds',
|
related_name='builds',
|
||||||
limit_choices_to={
|
limit_choices_to={
|
||||||
'assembly': True,
|
'assembly': True,
|
||||||
'active': True,
|
|
||||||
'virtual': False,
|
|
||||||
},
|
},
|
||||||
help_text=_('Select part to build'),
|
help_text=_('Select part to build'),
|
||||||
)
|
)
|
||||||
|
@ -390,12 +390,40 @@ class PurchaseOrderLineItemFilter(LineItemFilter):
|
|||||||
label=_('Supplier Part'),
|
label=_('Supplier Part'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
include_variants = rest_filters.BooleanFilter(
|
||||||
|
label=_('Include Variants'), method='filter_include_variants'
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_include_variants(self, queryset, name, value):
|
||||||
|
"""Filter by whether or not to include variants of the selected part.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- This filter does nothing by itself, and requires the 'base_part' filter to be set.
|
||||||
|
- Refer to the 'filter_base_part' method for more information.
|
||||||
|
"""
|
||||||
|
return queryset
|
||||||
|
|
||||||
base_part = rest_filters.ModelChoiceFilter(
|
base_part = rest_filters.ModelChoiceFilter(
|
||||||
queryset=Part.objects.filter(purchaseable=True),
|
queryset=Part.objects.filter(purchaseable=True),
|
||||||
field_name='part__part',
|
method='filter_base_part',
|
||||||
label=_('Internal Part'),
|
label=_('Internal Part'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def filter_base_part(self, queryset, name, base_part):
|
||||||
|
"""Filter by the 'base_part' attribute.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- If "include_variants" is True, include all variants of the selected part
|
||||||
|
- Otherwise, just filter by the selected part
|
||||||
|
"""
|
||||||
|
include_variants = str2bool(self.data.get('include_variants', False))
|
||||||
|
|
||||||
|
if include_variants:
|
||||||
|
parts = base_part.get_descendants(include_self=True)
|
||||||
|
return queryset.filter(part__part__in=parts)
|
||||||
|
else:
|
||||||
|
return queryset.filter(part__part=base_part)
|
||||||
|
|
||||||
pending = rest_filters.BooleanFilter(
|
pending = rest_filters.BooleanFilter(
|
||||||
method='filter_pending', label=_('Order Pending')
|
method='filter_pending', label=_('Order Pending')
|
||||||
)
|
)
|
||||||
@ -575,6 +603,47 @@ class SalesOrderFilter(OrderFilter):
|
|||||||
model = models.SalesOrder
|
model = models.SalesOrder
|
||||||
fields = ['customer']
|
fields = ['customer']
|
||||||
|
|
||||||
|
include_variants = rest_filters.BooleanFilter(
|
||||||
|
label=_('Include Variants'), method='filter_include_variants'
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_include_variants(self, queryset, name, value):
|
||||||
|
"""Filter by whether or not to include variants of the selected part.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- This filter does nothing by itself, and requires the 'part' filter to be set.
|
||||||
|
- Refer to the 'filter_part' method for more information.
|
||||||
|
"""
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
part = rest_filters.ModelChoiceFilter(
|
||||||
|
queryset=Part.objects.all(), field_name='part', method='filter_part'
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_part(self, queryset, name, part):
|
||||||
|
"""Filter SalesOrder by selected 'part'.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- If 'include_variants' is set to True, then all variants of the selected part will be included.
|
||||||
|
- Otherwise, just filter by the selected part.
|
||||||
|
"""
|
||||||
|
include_variants = str2bool(self.data.get('include_variants', False))
|
||||||
|
|
||||||
|
# Construct a queryset of parts to filter by
|
||||||
|
if include_variants:
|
||||||
|
parts = part.get_descendants(include_self=True)
|
||||||
|
else:
|
||||||
|
parts = Part.objects.filter(pk=part.pk)
|
||||||
|
|
||||||
|
# Now that we have a queryset of parts, find all the matching sales orders
|
||||||
|
line_items = models.SalesOrderLineItem.objects.filter(part__in=parts)
|
||||||
|
|
||||||
|
# Generate a list of ID values for the matching sales orders
|
||||||
|
sales_orders = line_items.values_list('order', flat=True).distinct()
|
||||||
|
|
||||||
|
# Now we have a list of matching IDs, filter the queryset
|
||||||
|
return queryset.filter(pk__in=sales_orders)
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderMixin:
|
class SalesOrderMixin:
|
||||||
"""Mixin class for SalesOrder endpoints."""
|
"""Mixin class for SalesOrder endpoints."""
|
||||||
@ -636,17 +705,6 @@ class SalesOrderList(SalesOrderMixin, DataExportViewMixin, ListCreateAPI):
|
|||||||
|
|
||||||
params = self.request.query_params
|
params = self.request.query_params
|
||||||
|
|
||||||
# Filter by "Part"
|
|
||||||
# Only return SalesOrder which have LineItem referencing the part
|
|
||||||
part = params.get('part', None)
|
|
||||||
|
|
||||||
if part is not None:
|
|
||||||
try:
|
|
||||||
part = Part.objects.get(pk=part)
|
|
||||||
queryset = queryset.filter(id__in=[so.id for so in part.sales_orders()])
|
|
||||||
except (Part.DoesNotExist, ValueError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Filter by 'date range'
|
# Filter by 'date range'
|
||||||
min_date = params.get('min_date', None)
|
min_date = params.get('min_date', None)
|
||||||
max_date = params.get('max_date', None)
|
max_date = params.get('max_date', None)
|
||||||
@ -903,10 +961,38 @@ class SalesOrderAllocationFilter(rest_filters.FilterSet):
|
|||||||
label=_('Order'),
|
label=_('Order'),
|
||||||
)
|
)
|
||||||
|
|
||||||
part = rest_filters.ModelChoiceFilter(
|
include_variants = rest_filters.BooleanFilter(
|
||||||
queryset=Part.objects.all(), field_name='item__part', label=_('Part')
|
label=_('Include Variants'), method='filter_include_variants'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def filter_include_variants(self, queryset, name, value):
|
||||||
|
"""Filter by whether or not to include variants of the selected part.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- This filter does nothing by itself, and requires the 'part' filter to be set.
|
||||||
|
- Refer to the 'filter_part' method for more information.
|
||||||
|
"""
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
part = rest_filters.ModelChoiceFilter(
|
||||||
|
queryset=Part.objects.all(), method='filter_part', label=_('Part')
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_part(self, queryset, name, part):
|
||||||
|
"""Filter by the 'part' attribute.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- If "include_variants" is True, include all variants of the selected part
|
||||||
|
- Otherwise, just filter by the selected part
|
||||||
|
"""
|
||||||
|
include_variants = str2bool(self.data.get('include_variants', False))
|
||||||
|
|
||||||
|
if include_variants:
|
||||||
|
parts = part.get_descendants(include_self=True)
|
||||||
|
return queryset.filter(item__part__in=parts)
|
||||||
|
else:
|
||||||
|
return queryset.filter(item__part=part)
|
||||||
|
|
||||||
outstanding = rest_filters.BooleanFilter(
|
outstanding = rest_filters.BooleanFilter(
|
||||||
label=_('Outstanding'), method='filter_outstanding'
|
label=_('Outstanding'), method='filter_outstanding'
|
||||||
)
|
)
|
||||||
@ -1072,6 +1158,46 @@ class ReturnOrderFilter(OrderFilter):
|
|||||||
model = models.ReturnOrder
|
model = models.ReturnOrder
|
||||||
fields = ['customer']
|
fields = ['customer']
|
||||||
|
|
||||||
|
include_variants = rest_filters.BooleanFilter(
|
||||||
|
label=_('Include Variants'), method='filter_include_variants'
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_include_variants(self, queryset, name, value):
|
||||||
|
"""Filter by whether or not to include variants of the selected part.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- This filter does nothing by itself, and requires the 'part' filter to be set.
|
||||||
|
- Refer to the 'filter_part' method for more information.
|
||||||
|
"""
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
part = rest_filters.ModelChoiceFilter(
|
||||||
|
queryset=Part.objects.all(), field_name='part', method='filter_part'
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_part(self, queryset, name, part):
|
||||||
|
"""Filter by selected 'part'.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- If 'include_variants' is set to True, then all variants of the selected part will be included.
|
||||||
|
- Otherwise, just filter by the selected part.
|
||||||
|
"""
|
||||||
|
include_variants = str2bool(self.data.get('include_variants', False))
|
||||||
|
|
||||||
|
if include_variants:
|
||||||
|
parts = part.get_descendants(include_self=True)
|
||||||
|
else:
|
||||||
|
parts = Part.objects.filter(pk=part.pk)
|
||||||
|
|
||||||
|
# Now that we have a queryset of parts, find all the matching return orders
|
||||||
|
line_items = models.ReturnOrderLineItem.objects.filter(item__part__in=parts)
|
||||||
|
|
||||||
|
# Generate a list of ID values for the matching return orders
|
||||||
|
return_orders = line_items.values_list('order', flat=True).distinct()
|
||||||
|
|
||||||
|
# Now we have a list of matching IDs, filter the queryset
|
||||||
|
return queryset.filter(pk__in=return_orders)
|
||||||
|
|
||||||
|
|
||||||
class ReturnOrderMixin:
|
class ReturnOrderMixin:
|
||||||
"""Mixin class for ReturnOrder endpoints."""
|
"""Mixin class for ReturnOrder endpoints."""
|
||||||
|
@ -223,8 +223,10 @@ export default function CompanyDetail(props: Readonly<CompanyDetailProps>) {
|
|||||||
label: t`Return Orders`,
|
label: t`Return Orders`,
|
||||||
icon: <IconTruckReturn />,
|
icon: <IconTruckReturn />,
|
||||||
hidden: !company?.is_customer,
|
hidden: !company?.is_customer,
|
||||||
content: company.pk && (
|
content: company.pk ? (
|
||||||
<ReturnOrderTable params={{ customer: company.pk }} />
|
<ReturnOrderTable customerId={company.pk} />
|
||||||
|
) : (
|
||||||
|
<Skeleton />
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -29,6 +29,7 @@ import {
|
|||||||
IconTestPipe,
|
IconTestPipe,
|
||||||
IconTools,
|
IconTools,
|
||||||
IconTruckDelivery,
|
IconTruckDelivery,
|
||||||
|
IconTruckReturn,
|
||||||
IconVersions
|
IconVersions
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
||||||
@ -43,7 +44,6 @@ import { DetailsField, DetailsTable } from '../../components/details/Details';
|
|||||||
import DetailsBadge from '../../components/details/DetailsBadge';
|
import DetailsBadge from '../../components/details/DetailsBadge';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
import NotesEditor from '../../components/editors/NotesEditor';
|
|
||||||
import { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
|
import { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
|
||||||
import { Thumbnail } from '../../components/images/Thumbnail';
|
import { Thumbnail } from '../../components/images/Thumbnail';
|
||||||
import {
|
import {
|
||||||
@ -98,6 +98,7 @@ import { PartVariantTable } from '../../tables/part/PartVariantTable';
|
|||||||
import { RelatedPartTable } from '../../tables/part/RelatedPartTable';
|
import { RelatedPartTable } from '../../tables/part/RelatedPartTable';
|
||||||
import { ManufacturerPartTable } from '../../tables/purchasing/ManufacturerPartTable';
|
import { ManufacturerPartTable } from '../../tables/purchasing/ManufacturerPartTable';
|
||||||
import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable';
|
import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable';
|
||||||
|
import { ReturnOrderTable } from '../../tables/sales/ReturnOrderTable';
|
||||||
import SalesOrderAllocationTable from '../../tables/sales/SalesOrderAllocationTable';
|
import SalesOrderAllocationTable from '../../tables/sales/SalesOrderAllocationTable';
|
||||||
import { SalesOrderTable } from '../../tables/sales/SalesOrderTable';
|
import { SalesOrderTable } from '../../tables/sales/SalesOrderTable';
|
||||||
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
||||||
@ -606,6 +607,7 @@ export default function PartDetail() {
|
|||||||
modelField="build"
|
modelField="build"
|
||||||
modelTarget={ModelType.build}
|
modelTarget={ModelType.build}
|
||||||
showBuildInfo
|
showBuildInfo
|
||||||
|
showPartInfo
|
||||||
allowEdit
|
allowEdit
|
||||||
/>
|
/>
|
||||||
</Accordion.Panel>
|
</Accordion.Panel>
|
||||||
@ -698,6 +700,13 @@ export default function PartDetail() {
|
|||||||
hidden: !part.salable,
|
hidden: !part.salable,
|
||||||
content: part.pk ? <SalesOrderTable partId={part.pk} /> : <Skeleton />
|
content: part.pk ? <SalesOrderTable partId={part.pk} /> : <Skeleton />
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'return_orders',
|
||||||
|
label: t`Return Orders`,
|
||||||
|
icon: <IconTruckReturn />,
|
||||||
|
hidden: !part.salable || !globalSettings.isSet('RETURNORDER_ENABLED'),
|
||||||
|
content: part.pk ? <ReturnOrderTable partId={part.pk} /> : <Skeleton />
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'stocktake',
|
name: 'stocktake',
|
||||||
label: t`Stock History`,
|
label: t`Stock History`,
|
||||||
@ -1093,7 +1102,7 @@ export default function PartDetail() {
|
|||||||
badges={badges}
|
badges={badges}
|
||||||
breadcrumbs={breadcrumbs}
|
breadcrumbs={breadcrumbs}
|
||||||
breadcrumbAction={() => {
|
breadcrumbAction={() => {
|
||||||
setTreeOpen(true);
|
setTreeOpen(true); // Open the category tree
|
||||||
}}
|
}}
|
||||||
editAction={editPart.open}
|
editAction={editPart.open}
|
||||||
editEnabled={user.hasChangeRole(UserRoles.part)}
|
editEnabled={user.hasChangeRole(UserRoles.part)}
|
||||||
|
@ -45,17 +45,30 @@ export default function BuildAllocatedStockTable({
|
|||||||
modelField?: string;
|
modelField?: string;
|
||||||
}>) {
|
}>) {
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
const table = useTable('buildallocatedstock');
|
const table = useTable(
|
||||||
|
!!partId ? 'buildallocatedstock-part' : 'buildallocatedstock'
|
||||||
|
);
|
||||||
|
|
||||||
const tableFilters: TableFilter[] = useMemo(() => {
|
const tableFilters: TableFilter[] = useMemo(() => {
|
||||||
return [
|
let filters: TableFilter[] = [
|
||||||
{
|
{
|
||||||
name: 'tracked',
|
name: 'tracked',
|
||||||
label: t`Allocated to Output`,
|
label: t`Allocated to Output`,
|
||||||
description: t`Show items allocated to a build output`
|
description: t`Show items allocated to a build output`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, []);
|
|
||||||
|
if (!!partId) {
|
||||||
|
filters.push({
|
||||||
|
name: 'include_variants',
|
||||||
|
type: 'boolean',
|
||||||
|
label: t`Include Variants`,
|
||||||
|
description: t`Include orders for part variants`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}, [partId]);
|
||||||
|
|
||||||
const tableColumns: TableColumn[] = useMemo(() => {
|
const tableColumns: TableColumn[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
@ -46,6 +46,8 @@ export function BuildOrderTable({
|
|||||||
parentBuildId?: number;
|
parentBuildId?: number;
|
||||||
salesOrderId?: number;
|
salesOrderId?: number;
|
||||||
}>) {
|
}>) {
|
||||||
|
const table = useTable(!!partId ? 'buildorder-part' : 'buildorder-index');
|
||||||
|
|
||||||
const tableColumns = useMemo(() => {
|
const tableColumns = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
ReferenceColumn({}),
|
ReferenceColumn({}),
|
||||||
@ -110,7 +112,7 @@ export function BuildOrderTable({
|
|||||||
const ownerFilters = useOwnerFilters();
|
const ownerFilters = useOwnerFilters();
|
||||||
|
|
||||||
const tableFilters: TableFilter[] = useMemo(() => {
|
const tableFilters: TableFilter[] = useMemo(() => {
|
||||||
return [
|
let filters: TableFilter[] = [
|
||||||
{
|
{
|
||||||
name: 'active',
|
name: 'active',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
@ -147,12 +149,22 @@ export function BuildOrderTable({
|
|||||||
choices: ownerFilters.choices
|
choices: ownerFilters.choices
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, [parentBuildId, projectCodeFilters.choices, ownerFilters.choices]);
|
|
||||||
|
// If we are filtering on a specific part, we can include the "include variants" filter
|
||||||
|
if (!!partId) {
|
||||||
|
filters.push({
|
||||||
|
name: 'include_variants',
|
||||||
|
type: 'boolean',
|
||||||
|
label: t`Include Variants`,
|
||||||
|
description: t`Include orders for part variants`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}, [partId, projectCodeFilters.choices, ownerFilters.choices]);
|
||||||
|
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
|
|
||||||
const table = useTable('buildorder');
|
|
||||||
|
|
||||||
const buildOrderFields = useBuildOrderFields({ create: true });
|
const buildOrderFields = useBuildOrderFields({ create: true });
|
||||||
|
|
||||||
const newBuild = useCreateApiFormModal({
|
const newBuild = useCreateApiFormModal({
|
||||||
|
@ -123,6 +123,12 @@ export default function PartPurchaseOrdersTable({
|
|||||||
label: t`Order Status`,
|
label: t`Order Status`,
|
||||||
description: t`Filter by order status`,
|
description: t`Filter by order status`,
|
||||||
choiceFunction: StatusFilterOptions(ModelType.purchaseorder)
|
choiceFunction: StatusFilterOptions(ModelType.purchaseorder)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'include_variants',
|
||||||
|
type: 'boolean',
|
||||||
|
label: t`Include Variants`,
|
||||||
|
description: t`Include orders for part variants`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -35,15 +35,21 @@ import {
|
|||||||
} from '../Filter';
|
} from '../Filter';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
export function ReturnOrderTable({ params }: Readonly<{ params?: any }>) {
|
export function ReturnOrderTable({
|
||||||
const table = useTable('return-orders');
|
partId,
|
||||||
|
customerId
|
||||||
|
}: Readonly<{
|
||||||
|
partId?: number;
|
||||||
|
customerId?: number;
|
||||||
|
}>) {
|
||||||
|
const table = useTable(!!partId ? 'returnorders-part' : 'returnorders-index');
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
|
|
||||||
const projectCodeFilters = useProjectCodeFilters();
|
const projectCodeFilters = useProjectCodeFilters();
|
||||||
const responsibleFilters = useOwnerFilters();
|
const responsibleFilters = useOwnerFilters();
|
||||||
|
|
||||||
const tableFilters: TableFilter[] = useMemo(() => {
|
const tableFilters: TableFilter[] = useMemo(() => {
|
||||||
return [
|
let filters: TableFilter[] = [
|
||||||
{
|
{
|
||||||
name: 'status',
|
name: 'status',
|
||||||
label: t`Status`,
|
label: t`Status`,
|
||||||
@ -69,7 +75,18 @@ export function ReturnOrderTable({ params }: Readonly<{ params?: any }>) {
|
|||||||
choices: responsibleFilters.choices
|
choices: responsibleFilters.choices
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, [projectCodeFilters.choices, responsibleFilters.choices]);
|
|
||||||
|
if (!!partId) {
|
||||||
|
filters.push({
|
||||||
|
name: 'include_variants',
|
||||||
|
type: 'boolean',
|
||||||
|
label: t`Include Variants`,
|
||||||
|
description: t`Include orders for part variants`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}, [partId, projectCodeFilters.choices, responsibleFilters.choices]);
|
||||||
|
|
||||||
const tableColumns = useMemo(() => {
|
const tableColumns = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
@ -143,7 +160,8 @@ export function ReturnOrderTable({ params }: Readonly<{ params?: any }>) {
|
|||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
props={{
|
props={{
|
||||||
params: {
|
params: {
|
||||||
...params,
|
part: partId,
|
||||||
|
customer: customerId,
|
||||||
customer_detail: true
|
customer_detail: true
|
||||||
},
|
},
|
||||||
tableFilters: tableFilters,
|
tableFilters: tableFilters,
|
||||||
|
@ -47,17 +47,30 @@ export default function SalesOrderAllocationTable({
|
|||||||
modelField?: string;
|
modelField?: string;
|
||||||
}>) {
|
}>) {
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
const table = useTable('salesorderallocations');
|
const table = useTable(
|
||||||
|
!!partId ? 'salesorderallocations-part' : 'salesorderallocations'
|
||||||
|
);
|
||||||
|
|
||||||
const tableFilters: TableFilter[] = useMemo(() => {
|
const tableFilters: TableFilter[] = useMemo(() => {
|
||||||
return [
|
let filters: TableFilter[] = [
|
||||||
{
|
{
|
||||||
name: 'outstanding',
|
name: 'outstanding',
|
||||||
label: t`Outstanding`,
|
label: t`Outstanding`,
|
||||||
description: t`Show outstanding allocations`
|
description: t`Show outstanding allocations`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, []);
|
|
||||||
|
if (!!partId) {
|
||||||
|
filters.push({
|
||||||
|
name: 'include_variants',
|
||||||
|
type: 'boolean',
|
||||||
|
label: t`Include Variants`,
|
||||||
|
description: t`Include orders for part variants`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}, [partId]);
|
||||||
|
|
||||||
const tableColumns: TableColumn[] = useMemo(() => {
|
const tableColumns: TableColumn[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
@ -43,14 +43,14 @@ export function SalesOrderTable({
|
|||||||
partId?: number;
|
partId?: number;
|
||||||
customerId?: number;
|
customerId?: number;
|
||||||
}>) {
|
}>) {
|
||||||
const table = useTable('sales-order');
|
const table = useTable(!!partId ? 'salesorder-part' : 'salesorder-index');
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
|
|
||||||
const projectCodeFilters = useProjectCodeFilters();
|
const projectCodeFilters = useProjectCodeFilters();
|
||||||
const responsibleFilters = useOwnerFilters();
|
const responsibleFilters = useOwnerFilters();
|
||||||
|
|
||||||
const tableFilters: TableFilter[] = useMemo(() => {
|
const tableFilters: TableFilter[] = useMemo(() => {
|
||||||
return [
|
let filters: TableFilter[] = [
|
||||||
{
|
{
|
||||||
name: 'status',
|
name: 'status',
|
||||||
label: t`Status`,
|
label: t`Status`,
|
||||||
@ -76,7 +76,18 @@ export function SalesOrderTable({
|
|||||||
choices: responsibleFilters.choices
|
choices: responsibleFilters.choices
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, [projectCodeFilters.choices, responsibleFilters.choices]);
|
|
||||||
|
if (!!partId) {
|
||||||
|
filters.push({
|
||||||
|
name: 'include_variants',
|
||||||
|
type: 'boolean',
|
||||||
|
label: t`Include Variants`,
|
||||||
|
description: t`Include orders for part variants`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}, [partId, projectCodeFilters.choices, responsibleFilters.choices]);
|
||||||
|
|
||||||
const salesOrderFields = useSalesOrderFields({});
|
const salesOrderFields = useSalesOrderFields({});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user