mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE-208,212,213): add transport-related fields and update form handling in PurchaseOrder forms
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { toast } from 'react-hot-toast';
|
import { toast } from 'react-hot-toast';
|
||||||
@@ -39,18 +39,6 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
useState('');
|
useState('');
|
||||||
|
|
||||||
// ===== TYPE DEFINITIONS =====
|
// ===== TYPE DEFINITIONS =====
|
||||||
interface PurchaseItemOptionType extends OptionType {
|
|
||||||
id: number;
|
|
||||||
quantity: number;
|
|
||||||
product: {
|
|
||||||
name: string;
|
|
||||||
product_category: string | { name: string };
|
|
||||||
uom: {
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ExpeditionVendorOptionType {
|
interface ExpeditionVendorOptionType {
|
||||||
value: number;
|
value: number;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -145,32 +133,29 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
const payload: CreateAcceptApprovalRequestPayload = {
|
const payload: CreateAcceptApprovalRequestPayload = {
|
||||||
notes: values.notes || '',
|
notes: values.notes || '',
|
||||||
items: (values.items || []).map((item) => ({
|
items:
|
||||||
purchase_item_id:
|
values.items?.map((formItem) => {
|
||||||
typeof item.purchase_item_id === 'string'
|
return {
|
||||||
? parseInt(item.purchase_item_id) || 0
|
purchase_item_id: formItem.purchase_item_id || 0,
|
||||||
: item.purchase_item_id || 0,
|
received_date: formItem.received_date || '',
|
||||||
received_date: item.received_date || '',
|
travel_number: formItem.travel_number || '',
|
||||||
travel_number: item.travel_number || '',
|
travel_document_path: formItem.travel_document_path || '',
|
||||||
travel_document_path: item.travel_document_path || '',
|
vehicle_number: formItem.vehicle_number || '',
|
||||||
vehicle_number: item.vehicle_number || '',
|
expedition_vendor_id: formItem.expedition_vendor_id || 0,
|
||||||
expedition_vendor_id:
|
|
||||||
typeof item.expedition_vendor_id === 'string'
|
|
||||||
? parseInt(item.expedition_vendor_id) || 0
|
|
||||||
: item.expedition_vendor_id || 0,
|
|
||||||
received_qty:
|
received_qty:
|
||||||
typeof item.received_qty === 'string'
|
typeof formItem.received_qty === 'string'
|
||||||
? parseFloat(item.received_qty) || 0
|
? parseFloat(formItem.received_qty) || 0
|
||||||
: item.received_qty || 0,
|
: formItem.received_qty || 0,
|
||||||
transport_per_item:
|
transport_per_item:
|
||||||
typeof item.transport_per_item === 'string'
|
typeof formItem.transport_per_item === 'string'
|
||||||
? parseFloat(item.transport_per_item) || 0
|
? parseFloat(formItem.transport_per_item) || 0
|
||||||
: item.transport_per_item || 0,
|
: formItem.transport_per_item || 0,
|
||||||
transport_total:
|
transport_total:
|
||||||
typeof item.transport_total === 'string'
|
typeof formItem.transport_total === 'string'
|
||||||
? parseFloat(item.transport_total) || 0
|
? parseFloat(formItem.transport_total) || 0
|
||||||
: item.transport_total || 0,
|
: formItem.transport_total || 0,
|
||||||
})),
|
};
|
||||||
|
}) || [],
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -192,13 +177,13 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
if (initialValues?.items) {
|
if (initialValues?.items) {
|
||||||
return initialValues.items.map((item) => ({
|
return initialValues.items.map((item) => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label: `${item.product.name} (${item.quantity} ${item.product.uom?.name || 'unit'})`,
|
label: item.product.name,
|
||||||
id: item.id,
|
id: item.id,
|
||||||
quantity: item.quantity,
|
quantity: item.sub_qty,
|
||||||
product: {
|
product: {
|
||||||
name: item.product.name,
|
name: item.product.name,
|
||||||
product_category: item.product.product_category,
|
product_category: item.product.product_category || '',
|
||||||
uom: item.product.uom,
|
uom: item.product.uom || { name: 'unit' },
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -206,33 +191,64 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
return [];
|
return [];
|
||||||
}, [initialValues?.items]);
|
}, [initialValues?.items]);
|
||||||
|
|
||||||
const expeditionVendors: ExpeditionVendorOptionType[] = useMemo(() => {
|
useEffect(() => {
|
||||||
return [];
|
if (purchaseItems.length > 0 && initialValues?.items) {
|
||||||
}, []);
|
const updatedItems = initialValues.items.map((item) => {
|
||||||
|
return {
|
||||||
|
purchase_item: null,
|
||||||
|
purchase_item_id: item.id,
|
||||||
|
received_date: item.received_date
|
||||||
|
? new Date(item.received_date).toISOString().split('T')[0]
|
||||||
|
: '',
|
||||||
|
travel_number: item.travel_number || '',
|
||||||
|
travel_document_path: item.travel_document_path || '',
|
||||||
|
vehicle_number: item.vehicle_number || '',
|
||||||
|
expedition_vendor: null,
|
||||||
|
expedition_vendor_id: 0,
|
||||||
|
received_qty: item.sub_qty ? item.sub_qty.toString() : '',
|
||||||
|
transport_per_item: '',
|
||||||
|
transport_total: '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
formik.setFieldValue('items', updatedItems);
|
||||||
|
}
|
||||||
|
}, [purchaseItems, initialValues]);
|
||||||
|
|
||||||
const getPurchaseItemOptions = useCallback(() => {
|
const expeditionVendors: ExpeditionVendorOptionType[] = useMemo(() => {
|
||||||
return purchaseItems;
|
return [
|
||||||
}, [purchaseItems]);
|
{
|
||||||
|
value: 1,
|
||||||
|
label: 'PT. Ekspedisi Sejahtera',
|
||||||
|
id: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 2,
|
||||||
|
label: 'PT. Jaya Trans Logistics',
|
||||||
|
id: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 3,
|
||||||
|
label: 'PT. Cargo Express Indonesia',
|
||||||
|
id: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 4,
|
||||||
|
label: 'PT. Fast Delivery Service',
|
||||||
|
id: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 5,
|
||||||
|
label: 'PT. Mitra Angkutan Sejahtera',
|
||||||
|
id: 5,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
const getExpeditionVendorOptions = useCallback(() => {
|
const getExpeditionVendorOptions = useCallback(() => {
|
||||||
return expeditionVendors;
|
return expeditionVendors;
|
||||||
}, [expeditionVendors]);
|
}, [expeditionVendors]);
|
||||||
|
|
||||||
// ===== FIELD CHANGE HANDLERS =====
|
// ===== FIELD CHANGE HANDLERS =====
|
||||||
const purchaseItemChangeHandler = (
|
|
||||||
idx: number,
|
|
||||||
val: OptionType | OptionType[] | null
|
|
||||||
) => {
|
|
||||||
const purchaseItem = val as PurchaseItemOptionType | null;
|
|
||||||
formik.setFieldTouched(`items.${idx}.purchase_item`, true);
|
|
||||||
formik.setFieldValue(`items.${idx}.purchase_item`, purchaseItem);
|
|
||||||
formik.setFieldTouched(`items.${idx}.purchase_item_id`, true);
|
|
||||||
formik.setFieldValue(
|
|
||||||
`items.${idx}.purchase_item_id`,
|
|
||||||
(purchaseItem as OptionType)?.value || 0
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const expeditionVendorChangeHandler = (
|
const expeditionVendorChangeHandler = (
|
||||||
idx: number,
|
idx: number,
|
||||||
val: OptionType | OptionType[] | null
|
val: OptionType | OptionType[] | null
|
||||||
@@ -298,14 +314,21 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
? 'Konfirmasi Penerimaan Produk'
|
? 'Konfirmasi Penerimaan Produk'
|
||||||
: 'Edit Penerimaan Produk'}
|
: 'Edit Penerimaan Produk'}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
{/* Debug Info */}
|
||||||
|
<div className='mb-4 p-4 bg-gray-100 rounded-lg text-xs'>
|
||||||
|
<div>Form Valid: {formik.isValid ? '✅' : '❌'}</div>
|
||||||
|
<div>Form Submitting: {formik.isSubmitting ? '✅' : '❌'}</div>
|
||||||
|
<div>Form Errors: {JSON.stringify(formik.errors, null, 2)}</div>
|
||||||
|
<div>Form Touched: {JSON.stringify(formik.touched, null, 2)}</div>
|
||||||
|
</div>
|
||||||
<div className='overflow-x-auto'>
|
<div className='overflow-x-auto'>
|
||||||
<table className='table'>
|
<table className='table'>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>Produk</th>
|
||||||
Item
|
<th>Jumlah</th>
|
||||||
<span className='text-error'>*</span>
|
<th>Satuan</th>
|
||||||
</th>
|
|
||||||
<th>
|
<th>
|
||||||
Tanggal Diterima
|
Tanggal Diterima
|
||||||
<span className='text-error'>*</span>
|
<span className='text-error'>*</span>
|
||||||
@@ -341,29 +364,53 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{formik.values.items?.map((item, idx) => {
|
{purchaseItems?.map((purchaseItem, idx) => {
|
||||||
purchaseItems.find((p) => p.value === item.purchase_item_id);
|
const formItem = formik.values.items?.[idx];
|
||||||
return (
|
return (
|
||||||
<tr key={`purchase-item-${idx}`}>
|
<tr key={`purchase-item-${idx}`}>
|
||||||
<td>
|
<td>
|
||||||
<SelectInput
|
<TextInput
|
||||||
required
|
name={`items.${idx}.product_name`}
|
||||||
isClearable={true}
|
type='text'
|
||||||
value={item.purchase_item}
|
value={purchaseItem?.product?.name || ''}
|
||||||
key={`purchase-item-${idx}`}
|
readOnly={true}
|
||||||
onChange={(val) => purchaseItemChangeHandler(idx, val)}
|
|
||||||
options={getPurchaseItemOptions()}
|
|
||||||
isError={
|
|
||||||
isRepeaterInputError(idx, 'purchase_item_id').isError
|
|
||||||
}
|
|
||||||
errorMessage={
|
|
||||||
isRepeaterInputError(idx, 'purchase_item_id')
|
|
||||||
.errorMessage
|
|
||||||
}
|
|
||||||
placeholder='Pilih Item...'
|
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'min-w-52 md:min-w-72 lg:min-w-80',
|
wrapper: 'min-w-52 md:min-w-72 lg:min-w-80',
|
||||||
}}
|
}}
|
||||||
|
disabled={true}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type='hidden'
|
||||||
|
name={`items.${idx}.purchase_item_id`}
|
||||||
|
value={purchaseItem?.value || 0}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<TextInput
|
||||||
|
name={`items.${idx}.quantity`}
|
||||||
|
type='text'
|
||||||
|
value={
|
||||||
|
purchaseItem?.quantity
|
||||||
|
? purchaseItem.quantity.toLocaleString('id-ID')
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
readOnly={true}
|
||||||
|
className={{
|
||||||
|
wrapper: 'min-w-32',
|
||||||
|
}}
|
||||||
|
disabled={true}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<TextInput
|
||||||
|
name={`items.${idx}.uom`}
|
||||||
|
type='text'
|
||||||
|
value={purchaseItem?.product?.uom?.name || ''}
|
||||||
|
readOnly={true}
|
||||||
|
className={{
|
||||||
|
wrapper: 'min-w-24',
|
||||||
|
}}
|
||||||
|
disabled={true}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -371,7 +418,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
required
|
required
|
||||||
isNestedModal={true}
|
isNestedModal={true}
|
||||||
name={`items.${idx}.received_date`}
|
name={`items.${idx}.received_date`}
|
||||||
value={item.received_date || ''}
|
value={formItem?.received_date || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
`items.${idx}.received_date`,
|
`items.${idx}.received_date`,
|
||||||
@@ -396,7 +443,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
required
|
required
|
||||||
name={`items.${idx}.travel_number`}
|
name={`items.${idx}.travel_number`}
|
||||||
type='text'
|
type='text'
|
||||||
value={item.travel_number || ''}
|
value={formItem?.travel_number || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
`items.${idx}.travel_number`,
|
`items.${idx}.travel_number`,
|
||||||
@@ -422,7 +469,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
required
|
required
|
||||||
name={`items.${idx}.travel_document_path`}
|
name={`items.${idx}.travel_document_path`}
|
||||||
type='text'
|
type='text'
|
||||||
value={item.travel_document_path || ''}
|
value={formItem?.travel_document_path || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
`items.${idx}.travel_document_path`,
|
`items.${idx}.travel_document_path`,
|
||||||
@@ -449,7 +496,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
required
|
required
|
||||||
name={`items.${idx}.vehicle_number`}
|
name={`items.${idx}.vehicle_number`}
|
||||||
type='text'
|
type='text'
|
||||||
value={item.vehicle_number || ''}
|
value={formItem?.vehicle_number || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
`items.${idx}.vehicle_number`,
|
`items.${idx}.vehicle_number`,
|
||||||
@@ -474,7 +521,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
isClearable={true}
|
isClearable={true}
|
||||||
value={item.expedition_vendor}
|
value={formItem?.expedition_vendor}
|
||||||
key={`expedition-vendor-${idx}`}
|
key={`expedition-vendor-${idx}`}
|
||||||
onChange={(val) =>
|
onChange={(val) =>
|
||||||
expeditionVendorChangeHandler(idx, val)
|
expeditionVendorChangeHandler(idx, val)
|
||||||
@@ -498,7 +545,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
<NumberInput
|
<NumberInput
|
||||||
required
|
required
|
||||||
name={`items.${idx}.received_qty`}
|
name={`items.${idx}.received_qty`}
|
||||||
value={item.received_qty || ''}
|
value={formItem?.received_qty || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handlePurchaseItemChange(
|
handlePurchaseItemChange(
|
||||||
idx,
|
idx,
|
||||||
@@ -527,7 +574,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
<NumberInput
|
<NumberInput
|
||||||
required
|
required
|
||||||
name={`items.${idx}.transport_per_item`}
|
name={`items.${idx}.transport_per_item`}
|
||||||
value={item.transport_per_item || ''}
|
value={formItem?.transport_per_item || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handlePurchaseItemChange(
|
handlePurchaseItemChange(
|
||||||
idx,
|
idx,
|
||||||
@@ -559,7 +606,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
<NumberInput
|
<NumberInput
|
||||||
required
|
required
|
||||||
name={`items.${idx}.transport_total`}
|
name={`items.${idx}.transport_total`}
|
||||||
value={item.transport_total || ''}
|
value={formItem?.transport_total || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handlePurchaseItemChange(
|
handlePurchaseItemChange(
|
||||||
idx,
|
idx,
|
||||||
|
|||||||
@@ -131,19 +131,18 @@ const PurchaseAcceptApprovalItemObjectSchema: Yup.ObjectSchema<PurchaseAcceptApp
|
|||||||
purchase_item: Yup.object({
|
purchase_item: Yup.object({
|
||||||
value: Yup.number().min(1).required(),
|
value: Yup.number().min(1).required(),
|
||||||
label: Yup.string().required(),
|
label: Yup.string().required(),
|
||||||
}).nullable(),
|
}).nullable().optional(),
|
||||||
purchase_item_id: Yup.number()
|
purchase_item_id: Yup.number()
|
||||||
.min(1, 'Purchase item is required!')
|
.min(1, 'Purchase item is required!')
|
||||||
.required('Purchase item is required!')
|
.required('Purchase item is required!')
|
||||||
|
.typeError('Purchase item is required!')
|
||||||
.test(
|
.test(
|
||||||
'is-valid-purchase-item',
|
'is-valid-purchase-item',
|
||||||
'Purchase item must be selected!',
|
'Purchase item must be selected!',
|
||||||
function (value) {
|
function (value) {
|
||||||
if (!this.parent.purchase_item) return true;
|
|
||||||
return Boolean(value && value > 0);
|
return Boolean(value && value > 0);
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
.typeError('Purchase item must be selected!'),
|
|
||||||
received_date: Yup.string()
|
received_date: Yup.string()
|
||||||
.required('Tanggal penerimaan wajib diisi!')
|
.required('Tanggal penerimaan wajib diisi!')
|
||||||
.typeError('Tanggal penerimaan wajib diisi!'),
|
.typeError('Tanggal penerimaan wajib diisi!'),
|
||||||
|
|||||||
@@ -405,12 +405,12 @@ const PurchaseOrderDetail = ({
|
|||||||
cell: (props) => formatNumber(props.getValue() as number),
|
cell: (props) => formatNumber(props.getValue() as number),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'price',
|
accessorKey: 'transport_per_item',
|
||||||
header: 'Transport /Item',
|
header: 'Transport /Item',
|
||||||
cell: (props) => formatCurrency(props.getValue() as number),
|
cell: (props) => formatCurrency(props.getValue() as number),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'total_price',
|
accessorKey: 'transport_total',
|
||||||
header: 'Transport Total',
|
header: 'Transport Total',
|
||||||
cell: (props) => formatCurrency(props.getValue() as number),
|
cell: (props) => formatCurrency(props.getValue() as number),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export class AcceptApprovalService extends BaseApiService<
|
|||||||
payload: CreateAcceptApprovalRequestPayload
|
payload: CreateAcceptApprovalRequestPayload
|
||||||
): Promise<BaseApiResponse<{ message: string }> | undefined> {
|
): Promise<BaseApiResponse<{ message: string }> | undefined> {
|
||||||
return await this.customRequest<BaseApiResponse<{ message: string }>>(
|
return await this.customRequest<BaseApiResponse<{ message: string }>>(
|
||||||
`${purchaseRequestId}/approvals/receipts`,
|
`${purchaseRequestId}/receipts`,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
payload,
|
payload,
|
||||||
|
|||||||
Vendored
+5
@@ -38,6 +38,11 @@ export type PurchaseItem = {
|
|||||||
travel_number_docs?: string | null;
|
travel_number_docs?: string | null;
|
||||||
travel_document_path?: string | null;
|
travel_document_path?: string | null;
|
||||||
vehicle_number?: string | null;
|
vehicle_number?: string | null;
|
||||||
|
expedition_vendor_id?: number | null;
|
||||||
|
expedition_vendor_name?: string | null;
|
||||||
|
received_qty?: number | null;
|
||||||
|
transport_per_item?: number | null;
|
||||||
|
transport_total?: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BasePurchase = {
|
export type BasePurchase = {
|
||||||
|
|||||||
Reference in New Issue
Block a user