mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Added various PO fixes (#6483)
* Added various PO fixes * Add auto-pricing and merge items functionality to PurchaseOrderLineItem * Bump api version to v173 * Add po line item create/update tests
This commit is contained in:
parent
55c64b546f
commit
7694092935
@ -1,11 +1,15 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 172
|
||||
INVENTREE_API_VERSION = 173
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v173 - 2024-02-20 : https://github.com/inventree/InvenTree/pull/6483
|
||||
- Adds "merge_items" to the PurchaseOrderLine create API endpoint
|
||||
- Adds "auto_pricing" to the PurchaseOrderLine create/update API endpoint
|
||||
|
||||
v172 - 2024-02-20 : https://github.com/inventree/InvenTree/pull/6526
|
||||
- Adds "enabled" field to the PartTestTemplate API endpoint
|
||||
- Adds "enabled" filter to the PartTestTemplate list
|
||||
|
@ -1,5 +1,8 @@
|
||||
"""JSON API for the Order app."""
|
||||
|
||||
from decimal import Decimal
|
||||
from typing import cast
|
||||
|
||||
from django.contrib.auth import authenticate, login
|
||||
from django.db import transaction
|
||||
from django.db.models import F, Q
|
||||
@ -481,6 +484,14 @@ class PurchaseOrderLineItemMixin:
|
||||
|
||||
return self.serializer_class(*args, **kwargs)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
"""Override the perform_update method to auto-update pricing if required."""
|
||||
super().perform_update(serializer)
|
||||
|
||||
# possibly auto-update pricing based on the supplier part pricing data
|
||||
if serializer.validated_data.get('auto_pricing', True):
|
||||
serializer.instance.update_pricing()
|
||||
|
||||
|
||||
class PurchaseOrderLineItemList(
|
||||
PurchaseOrderLineItemMixin, APIDownloadMixin, ListCreateDestroyAPIView
|
||||
@ -493,6 +504,44 @@ class PurchaseOrderLineItemList(
|
||||
|
||||
filterset_class = PurchaseOrderLineItemFilter
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""Create or update a new PurchaseOrderLineItem object."""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
data = cast(dict, serializer.validated_data)
|
||||
|
||||
# possibly merge duplicate items
|
||||
line_item = None
|
||||
if data.get('merge_items', True):
|
||||
other_line = models.PurchaseOrderLineItem.objects.filter(
|
||||
part=data.get('part'),
|
||||
order=data.get('order'),
|
||||
target_date=data.get('target_date'),
|
||||
destination=data.get('destination'),
|
||||
).first()
|
||||
|
||||
if other_line is not None:
|
||||
other_line.quantity += Decimal(data.get('quantity', 0))
|
||||
other_line.save()
|
||||
|
||||
line_item = other_line
|
||||
|
||||
# otherwise create a new line item
|
||||
if line_item is None:
|
||||
line_item = serializer.save()
|
||||
|
||||
# possibly auto-update pricing based on the supplier part pricing data
|
||||
if data.get('auto_pricing', True) and isinstance(
|
||||
line_item, models.PurchaseOrderLineItem
|
||||
):
|
||||
line_item.update_pricing()
|
||||
|
||||
serializer = serializers.PurchaseOrderLineItemSerializer(line_item)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(
|
||||
serializer.data, status=status.HTTP_201_CREATED, headers=headers
|
||||
)
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
"""Additional filtering options."""
|
||||
params = self.request.query_params
|
||||
|
@ -1439,6 +1439,17 @@ class PurchaseOrderLineItem(OrderLineItem):
|
||||
r = self.quantity - self.received
|
||||
return max(r, 0)
|
||||
|
||||
def update_pricing(self):
|
||||
"""Update pricing information based on the supplier part data."""
|
||||
if self.part:
|
||||
price = self.part.get_price(self.quantity)
|
||||
|
||||
if price is None:
|
||||
return
|
||||
|
||||
self.purchase_price = Decimal(price) / Decimal(self.quantity)
|
||||
self.save()
|
||||
|
||||
|
||||
class PurchaseOrderExtraLine(OrderExtraLine):
|
||||
"""Model for a single ExtraLine in a PurchaseOrder.
|
||||
|
@ -340,11 +340,13 @@ class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
|
||||
'received',
|
||||
'purchase_price',
|
||||
'purchase_price_currency',
|
||||
'auto_pricing',
|
||||
'destination',
|
||||
'destination_detail',
|
||||
'target_date',
|
||||
'total_price',
|
||||
'link',
|
||||
'merge_items',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -362,6 +364,10 @@ class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
|
||||
if order_detail is not True:
|
||||
self.fields.pop('order_detail')
|
||||
|
||||
def skip_create_fields(self):
|
||||
"""Return a list of fields to skip when creating a new object."""
|
||||
return ['auto_pricing', 'merge_items'] + super().skip_create_fields()
|
||||
|
||||
@staticmethod
|
||||
def annotate_queryset(queryset):
|
||||
"""Add some extra annotations to this queryset.
|
||||
@ -419,6 +425,14 @@ class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
|
||||
|
||||
purchase_price = InvenTreeMoneySerializer(allow_null=True)
|
||||
|
||||
auto_pricing = serializers.BooleanField(
|
||||
label=_('Auto Pricing'),
|
||||
help_text=_(
|
||||
'Automatically calculate purchase price based on supplier part data'
|
||||
),
|
||||
default=True,
|
||||
)
|
||||
|
||||
destination_detail = stock.serializers.LocationBriefSerializer(
|
||||
source='get_destination', read_only=True
|
||||
)
|
||||
@ -429,6 +443,14 @@ class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
|
||||
|
||||
order_detail = PurchaseOrderSerializer(source='order', read_only=True, many=False)
|
||||
|
||||
merge_items = serializers.BooleanField(
|
||||
label=_('Merge Items'),
|
||||
help_text=_(
|
||||
'Merge items with the same part, destination and target date into one line item'
|
||||
),
|
||||
default=True,
|
||||
)
|
||||
|
||||
def validate(self, data):
|
||||
"""Custom validation for the serializer.
|
||||
|
||||
|
@ -14,7 +14,7 @@ from icalendar import Calendar
|
||||
from rest_framework import status
|
||||
|
||||
from common.settings import currency_codes
|
||||
from company.models import Company
|
||||
from company.models import Company, SupplierPart, SupplierPriceBreak
|
||||
from InvenTree.status_codes import (
|
||||
PurchaseOrderStatus,
|
||||
ReturnOrderLineStatus,
|
||||
@ -675,6 +675,94 @@ class PurchaseOrderLineItemTest(OrderTest):
|
||||
# We should have 2 less PurchaseOrderLineItems after deletign them
|
||||
self.assertEqual(models.PurchaseOrderLineItem.objects.count(), n - 2)
|
||||
|
||||
def test_po_line_merge_pricing(self):
|
||||
"""Test that we can create a new PurchaseOrderLineItem via the API."""
|
||||
self.assignRole('purchase_order.add')
|
||||
self.generate_exchange_rates()
|
||||
|
||||
su = Company.objects.get(pk=1)
|
||||
sp = SupplierPart.objects.get(pk=1)
|
||||
po = models.PurchaseOrder.objects.create(supplier=su, reference='PO-1234567890')
|
||||
SupplierPriceBreak.objects.create(part=sp, quantity=1, price=Money(1, 'USD'))
|
||||
SupplierPriceBreak.objects.create(part=sp, quantity=10, price=Money(0.5, 'USD'))
|
||||
|
||||
li1 = self.post(
|
||||
reverse('api-po-line-list'),
|
||||
{
|
||||
'order': po.pk,
|
||||
'part': sp.pk,
|
||||
'quantity': 1,
|
||||
'auto_pricing': True,
|
||||
'merge_items': False,
|
||||
},
|
||||
expected_code=201,
|
||||
).json()
|
||||
self.assertEqual(float(li1['purchase_price']), 1)
|
||||
|
||||
li2 = self.post(
|
||||
reverse('api-po-line-list'),
|
||||
{
|
||||
'order': po.pk,
|
||||
'part': sp.pk,
|
||||
'quantity': 10,
|
||||
'auto_pricing': True,
|
||||
'merge_items': False,
|
||||
},
|
||||
expected_code=201,
|
||||
).json()
|
||||
self.assertEqual(float(li2['purchase_price']), 0.5)
|
||||
|
||||
# test that items where not merged
|
||||
self.assertNotEqual(li1['pk'], li2['pk'])
|
||||
|
||||
li3 = self.post(
|
||||
reverse('api-po-line-list'),
|
||||
{
|
||||
'order': po.pk,
|
||||
'part': sp.pk,
|
||||
'quantity': 9,
|
||||
'auto_pricing': True,
|
||||
'merge_items': True,
|
||||
},
|
||||
expected_code=201,
|
||||
).json()
|
||||
|
||||
# test that items where merged
|
||||
self.assertEqual(li1['pk'], li3['pk'])
|
||||
|
||||
# test that price was recalculated
|
||||
self.assertEqual(float(li3['purchase_price']), 0.5)
|
||||
|
||||
# test that pricing will be not recalculated if auto_pricing is False
|
||||
li4 = self.post(
|
||||
reverse('api-po-line-list'),
|
||||
{
|
||||
'order': po.pk,
|
||||
'part': sp.pk,
|
||||
'quantity': 1,
|
||||
'auto_pricing': False,
|
||||
'purchase_price': 0.5,
|
||||
'merge_items': False,
|
||||
},
|
||||
expected_code=201,
|
||||
).json()
|
||||
self.assertEqual(float(li4['purchase_price']), 0.5)
|
||||
|
||||
# test that pricing is correctly recalculated if auto_pricing is True for update
|
||||
li5 = self.patch(
|
||||
reverse('api-po-line-detail', kwargs={'pk': li4['pk']}),
|
||||
{**li4, 'quantity': 5, 'auto_pricing': False},
|
||||
expected_code=200,
|
||||
).json()
|
||||
self.assertEqual(float(li5['purchase_price']), 0.5)
|
||||
|
||||
li5 = self.patch(
|
||||
reverse('api-po-line-detail', kwargs={'pk': li4['pk']}),
|
||||
{**li4, 'quantity': 5, 'auto_pricing': True},
|
||||
expected_code=200,
|
||||
).json()
|
||||
self.assertEqual(float(li5['purchase_price']), 1)
|
||||
|
||||
|
||||
class PurchaseOrderDownloadTest(OrderTest):
|
||||
"""Unit tests for downloading PurchaseOrder data via the API endpoint."""
|
||||
|
@ -225,7 +225,7 @@ function createPurchaseOrder(options={}) {
|
||||
};
|
||||
}
|
||||
|
||||
constructForm('{% url "api-po-list" %}', {
|
||||
constructForm('{% url "api-po-list" %}?supplier_detail=true', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
groups: groups,
|
||||
@ -268,7 +268,6 @@ function duplicatePurchaseOrder(order_id, options={}) {
|
||||
|
||||
/* Construct a set of fields for the PurchaseOrderLineItem form */
|
||||
function poLineItemFields(options={}) {
|
||||
|
||||
var fields = {
|
||||
order: {
|
||||
filters: {
|
||||
@ -286,8 +285,6 @@ function poLineItemFields(options={}) {
|
||||
// If the pack_quantity != 1, add a note to the field
|
||||
var pack_quantity = 1;
|
||||
var units = '';
|
||||
var supplier_part_id = value;
|
||||
var quantity = getFormFieldValue('quantity', {}, opts);
|
||||
|
||||
// Remove any existing note fields
|
||||
$(opts.modal).find('#info-pack-size').remove();
|
||||
@ -314,37 +311,6 @@ function poLineItemFields(options={}) {
|
||||
var txt = `<span class='fas fa-info-circle icon-blue'></span> {% trans "Pack Quantity" %}: ${formatDecimal(pack_quantity)} ${units}`;
|
||||
$(opts.modal).find('#hint_id_quantity').after(`<div class='form-info-message' id='info-pack-size'>${txt}</div>`);
|
||||
}
|
||||
}).then(function() {
|
||||
// Update pricing data (if available)
|
||||
if (options.update_pricing) {
|
||||
inventreeGet(
|
||||
'{% url "api-part-supplier-price-list" %}',
|
||||
{
|
||||
part: supplier_part_id,
|
||||
ordering: 'quantity',
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
// Returned prices are in increasing order of quantity
|
||||
if (response.length > 0) {
|
||||
let index = 0;
|
||||
|
||||
for (var idx = 0; idx < response.length; idx++) {
|
||||
if (response[idx].quantity > quantity) {
|
||||
break;
|
||||
}
|
||||
|
||||
index = idx;
|
||||
}
|
||||
|
||||
// Update price and currency data in the form
|
||||
updateFieldValue('purchase_price', response[index].price, {}, opts);
|
||||
updateFieldValue('purchase_price_currency', response[index].price_currency, {}, opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
secondary: {
|
||||
@ -377,10 +343,20 @@ function poLineItemFields(options={}) {
|
||||
reference: {},
|
||||
purchase_price: {
|
||||
icon: 'fa-dollar-sign',
|
||||
onEdit: function(value, name, field, opts) {
|
||||
updateFieldValue('auto_pricing', value === '', {}, opts);
|
||||
}
|
||||
},
|
||||
purchase_price_currency: {
|
||||
icon: 'fa-coins',
|
||||
},
|
||||
auto_pricing: {
|
||||
onEdit: function(value, name, field, opts) {
|
||||
if (value) {
|
||||
updateFieldValue('purchase_price', '', {}, opts);
|
||||
}
|
||||
}
|
||||
},
|
||||
target_date: {
|
||||
icon: 'fa-calendar-alt',
|
||||
},
|
||||
@ -411,6 +387,10 @@ function poLineItemFields(options={}) {
|
||||
fields.target_date.value = options.target_date;
|
||||
}
|
||||
|
||||
if (options.create) {
|
||||
fields.merge_items = {};
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
@ -425,6 +405,7 @@ function createPurchaseOrderLineItem(order, options={}) {
|
||||
currency: options.currency,
|
||||
target_date: options.target_date,
|
||||
update_pricing: true,
|
||||
create: true,
|
||||
});
|
||||
|
||||
constructForm('{% url "api-po-line-list" %}', {
|
||||
@ -697,6 +678,15 @@ function orderParts(parts_list, options={}) {
|
||||
}
|
||||
);
|
||||
|
||||
const merge_item_input = constructField(
|
||||
`merge_item_${pk}`,
|
||||
{
|
||||
type: 'boolean',
|
||||
value: true,
|
||||
},
|
||||
{ hideLabels: true },
|
||||
);
|
||||
|
||||
let buttons = '';
|
||||
|
||||
if (parts.length > 1) {
|
||||
@ -723,6 +713,7 @@ function orderParts(parts_list, options={}) {
|
||||
<td id='td_supplier_part_${pk}'>${supplier_part_input}</td>
|
||||
<td id='td_order_${pk}'>${purchase_order_input}</td>
|
||||
<td id='td_quantity_${pk}'>${quantity_input}</td>
|
||||
<td id='td_merge_item_${pk}'>${merge_item_input}</td>
|
||||
<td id='td_actions_${pk}'>${buttons}</td>
|
||||
</tr>`;
|
||||
|
||||
@ -761,6 +752,7 @@ function orderParts(parts_list, options={}) {
|
||||
<th style='min-width: 300px;'>{% trans "Supplier Part" %}</th>
|
||||
<th style='min-width: 300px;'>{% trans "Purchase Order" %}</th>
|
||||
<th style='min-width: 50px;'>{% trans "Quantity" %}</th>
|
||||
<th style='min-width: 50px;'>{% trans "Merge" %}</th>
|
||||
<th><!-- Actions --></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -838,6 +830,10 @@ function orderParts(parts_list, options={}) {
|
||||
success: function(response) {
|
||||
pack_quantity = response.pack_quantity_native || 1;
|
||||
units = response.part_detail.units || '';
|
||||
if(response.supplier) {
|
||||
order_filters.supplier = response.supplier;
|
||||
options.supplier = response.supplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
).then(function() {
|
||||
@ -926,6 +922,7 @@ function orderParts(parts_list, options={}) {
|
||||
quantity: getFormFieldValue(`quantity_${pk}`, {type: 'decimal'}, opts),
|
||||
part: getFormFieldValue(`part_${pk}`, {}, opts),
|
||||
order: getFormFieldValue(`order_${pk}`, {}, opts),
|
||||
merge_items: getFormFieldValue(`merge_item_${pk}`, {type: 'boolean'}, opts),
|
||||
};
|
||||
|
||||
// Duplicate the form options, to prevent 'field_suffix' override
|
||||
@ -984,7 +981,7 @@ function orderParts(parts_list, options={}) {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
// Launch dialog to create new purchase order
|
||||
createPurchaseOrder({
|
||||
const poOptions = {
|
||||
onSuccess: function(response) {
|
||||
setRelatedFieldData(
|
||||
`order_${pk}`,
|
||||
@ -992,7 +989,14 @@ function orderParts(parts_list, options={}) {
|
||||
opts
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(options.supplier) {
|
||||
poOptions.supplier = options.supplier;
|
||||
poOptions.hide_supplier = true;
|
||||
}
|
||||
|
||||
createPurchaseOrder(poOptions);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -144,24 +144,24 @@ export function ApiFormField({
|
||||
);
|
||||
|
||||
// Coerce the value to a numerical value
|
||||
const numericalValue: number | undefined = useMemo(() => {
|
||||
let val = 0;
|
||||
const numericalValue: number | '' = useMemo(() => {
|
||||
let val: number | '' = 0;
|
||||
|
||||
switch (definition.field_type) {
|
||||
case 'integer':
|
||||
val = parseInt(value) ?? 0;
|
||||
val = parseInt(value) ?? '';
|
||||
break;
|
||||
case 'decimal':
|
||||
case 'float':
|
||||
case 'number':
|
||||
val = parseFloat(value) ?? 0;
|
||||
val = parseFloat(value) ?? '';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (isNaN(val) || !isFinite(val)) {
|
||||
val = 0;
|
||||
val = '';
|
||||
}
|
||||
|
||||
return val;
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
IconUser,
|
||||
IconUsers
|
||||
} from '@tabler/icons-react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
ApiFormAdjustFilterType,
|
||||
@ -20,45 +21,76 @@ import {
|
||||
/*
|
||||
* Construct a set of fields for creating / editing a PurchaseOrderLineItem instance
|
||||
*/
|
||||
export function purchaseOrderLineItemFields() {
|
||||
let fields: ApiFormFieldSet = {
|
||||
order: {
|
||||
filters: {
|
||||
supplier_detail: true
|
||||
},
|
||||
hidden: true
|
||||
},
|
||||
part: {
|
||||
filters: {
|
||||
part_detail: true,
|
||||
supplier_detail: true
|
||||
},
|
||||
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
||||
// TODO: Adjust part based on the supplier associated with the supplier
|
||||
return value.filters;
|
||||
}
|
||||
},
|
||||
quantity: {},
|
||||
reference: {},
|
||||
purchase_price: {
|
||||
icon: <IconCurrencyDollar />
|
||||
},
|
||||
purchase_price_currency: {
|
||||
icon: <IconCoins />
|
||||
},
|
||||
target_date: {
|
||||
icon: <IconCalendar />
|
||||
},
|
||||
destination: {
|
||||
icon: <IconSitemap />
|
||||
},
|
||||
notes: {
|
||||
icon: <IconNotes />
|
||||
},
|
||||
link: {
|
||||
icon: <IconLink />
|
||||
export function usePurchaseOrderLineItemFields({
|
||||
create
|
||||
}: {
|
||||
create?: boolean;
|
||||
}) {
|
||||
const [purchasePrice, setPurchasePrice] = useState<string>('');
|
||||
const [autoPricing, setAutoPricing] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (autoPricing) {
|
||||
setPurchasePrice('');
|
||||
}
|
||||
};
|
||||
}, [autoPricing]);
|
||||
|
||||
useEffect(() => {
|
||||
setAutoPricing(purchasePrice === '');
|
||||
}, [purchasePrice]);
|
||||
|
||||
const fields = useMemo(() => {
|
||||
const fields: ApiFormFieldSet = {
|
||||
order: {
|
||||
filters: {
|
||||
supplier_detail: true
|
||||
},
|
||||
hidden: true
|
||||
},
|
||||
part: {
|
||||
filters: {
|
||||
part_detail: true,
|
||||
supplier_detail: true
|
||||
},
|
||||
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
||||
// TODO: Adjust part based on the supplier associated with the supplier
|
||||
return value.filters;
|
||||
}
|
||||
},
|
||||
quantity: {},
|
||||
reference: {},
|
||||
purchase_price: {
|
||||
icon: <IconCurrencyDollar />,
|
||||
value: purchasePrice,
|
||||
onValueChange: setPurchasePrice
|
||||
},
|
||||
purchase_price_currency: {
|
||||
icon: <IconCoins />
|
||||
},
|
||||
auto_pricing: {
|
||||
value: autoPricing,
|
||||
onValueChange: setAutoPricing
|
||||
},
|
||||
target_date: {
|
||||
icon: <IconCalendar />
|
||||
},
|
||||
destination: {
|
||||
icon: <IconSitemap />
|
||||
},
|
||||
notes: {
|
||||
icon: <IconNotes />
|
||||
},
|
||||
link: {
|
||||
icon: <IconLink />
|
||||
}
|
||||
};
|
||||
|
||||
if (create) {
|
||||
fields['merge_items'] = {};
|
||||
}
|
||||
|
||||
return fields;
|
||||
}, [create, autoPricing, purchasePrice]);
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import { RenderStockLocation } from '../../components/render/Stock';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { purchaseOrderLineItemFields } from '../../forms/PurchaseOrderForms';
|
||||
import { usePurchaseOrderLineItemFields } from '../../forms/PurchaseOrderForms';
|
||||
import { getDetailUrl } from '../../functions/urls';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
@ -178,7 +178,7 @@ export function PurchaseOrderLineItemTable({
|
||||
const newLine = useCreateApiFormModal({
|
||||
url: ApiEndpoints.purchase_order_line_list,
|
||||
title: t`Add Line Item`,
|
||||
fields: purchaseOrderLineItemFields(),
|
||||
fields: usePurchaseOrderLineItemFields({ create: true }),
|
||||
initialData: {
|
||||
order: orderId
|
||||
},
|
||||
@ -193,7 +193,7 @@ export function PurchaseOrderLineItemTable({
|
||||
url: ApiEndpoints.purchase_order_line_list,
|
||||
pk: selectedLine,
|
||||
title: t`Edit Line Item`,
|
||||
fields: purchaseOrderLineItemFields(),
|
||||
fields: usePurchaseOrderLineItemFields({}),
|
||||
onFormSuccess: table.refreshTable
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user