From ae91e17ac0d605d597b0c379aae9705c4dd2818a Mon Sep 17 00:00:00 2001 From: rstubryan Date: Mon, 3 Nov 2025 11:29:43 +0700 Subject: [PATCH] refactor(FE-207,212): update PurchaseRequestForm schema and validation, enforce required fields and improve data handling for purchase items --- .../form/PurchaseRequestForm.schema.ts | 217 +++++++++--------- .../purchase/form/PurchaseRequestForm.tsx | 157 ++++++------- src/types/api/purchase/purchase.d.ts | 3 +- 3 files changed, 182 insertions(+), 195 deletions(-) diff --git a/src/components/pages/purchase/form/PurchaseRequestForm.schema.ts b/src/components/pages/purchase/form/PurchaseRequestForm.schema.ts index cc53de7a..e15dae8d 100644 --- a/src/components/pages/purchase/form/PurchaseRequestForm.schema.ts +++ b/src/components/pages/purchase/form/PurchaseRequestForm.schema.ts @@ -1,62 +1,106 @@ import * as Yup from 'yup'; -import { Supplier } from '@/types/api/master-data/supplier'; -import { Warehouse } from '@/types/api/master-data/warehouse'; -import { CreatePurchaseRequestPayload } from '@/types/api/purchase/purchase'; -import { Product } from '@/types/api/master-data/product'; -import { ProductWarehouse } from '@/types/api/inventory/product-warehouse'; +import { Purchase } from '@/types/api/purchase/purchase'; -export const PurchaseRequestFormSchema = Yup.object({ - supplier: Yup.object({ - value: Yup.number().min(1).required(), - label: Yup.string().required(), - }).nullable(), - supplier_id: Yup.number() - .required('Supplier wajib diisi!') - .min(1, 'Supplier wajib diisi!') - .typeError('Supplier wajib diisi!'), - credit_term: Yup.number() - .required('Termin kredit wajib diisi!') - .min(1, 'Termin kredit tidak boleh negatif!') - .typeError('Termin kredit harus berupa angka!'), - notes: Yup.string().optional().nullable(), - purchase_items: Yup.array() - .of( - Yup.object({ - warehouse: Yup.object({ - value: Yup.number().min(1).required(), - label: Yup.string().required(), - }).nullable(), - warehouse_id: Yup.number() - .required('Warehouse wajib diisi!') - .min(1, 'Warehouse wajib diisi!') - .typeError('Warehouse harus berupa angka!'), - product: Yup.object({ - value: Yup.number().min(1).required(), - label: Yup.string().required(), - }).nullable(), - product_id: Yup.number() - .required('Produk wajib diisi!') - .min(1, 'Produk wajib diisi!') - .typeError('Produk harus berupa angka!'), - product_warehouse: Yup.object({ - value: Yup.number().min(1).required(), - label: Yup.string().required(), - }).nullable(), - product_warehouse_id: Yup.number().optional().nullable(), - sub_qty: Yup.number() - .required('Sub Qty wajib diisi!') - .min(0.001, 'Sub Qty tidak boleh negatif!') - .typeError('Sub Qty harus berupa angka!'), - price: Yup.number() - .required('Harga wajib diisi!') - .min(0, 'Harga tidak boleh negatif!') - .typeError('Harga harus berupa angka!'), - }) - ) - .min(1, 'Minimal harus ada 1 item pembelian!') - .required('Item pembelian wajib diisi!') - .typeError('Item pembelian wajib diisi!'), -}); +type PurchaseRequestFormSchemaType = { + supplier?: { + value: number; + label: string; + } | null; + supplier_id: number; + credit_term: number | string; + notes: string | null; + purchase_items: { + warehouse?: { + value: number; + label: string; + } | null; + warehouse_id: number; + product?: { + value: number; + label: string; + } | null; + product_id: number; + product_warehouse?: { + value: number; + label: string; + } | null; + product_warehouse_id: number; + sub_qty: number | string; + }[]; +}; + +export type PurchaseItemSchema = { + warehouse?: { + value: number; + label: string; + } | null; + warehouse_id: number; + product?: { + value: number; + label: string; + } | null; + product_id: number; + product_warehouse?: { + value: number; + label: string; + } | null; + product_warehouse_id: number; + sub_qty: number | string; +}; + +const PurchaseItemObjectSchema: Yup.ObjectSchema = + Yup.object({ + warehouse: Yup.object({ + value: Yup.number().min(1).required(), + label: Yup.string().required(), + }).nullable(), + warehouse_id: Yup.number() + .required('Warehouse wajib diisi!') + .min(1, 'Warehouse wajib diisi!') + .typeError('Warehouse harus berupa angka!'), + product: Yup.object({ + value: Yup.number().min(1).required(), + label: Yup.string().required(), + }).nullable(), + product_id: Yup.number() + .required('Produk wajib diisi!') + .min(1, 'Produk wajib diisi!') + .typeError('Produk harus berupa angka!'), + product_warehouse: Yup.object({ + value: Yup.number().min(1).required(), + label: Yup.string().required(), + }).nullable(), + product_warehouse_id: Yup.number() + .required('Product Warehouse wajib diisi!') + .min(0.001, 'Product Warehouse tidak boleh negatif!') + .typeError('Product Warehouse harus berupa angka!'), + sub_qty: Yup.number() + .required('Sub Qty wajib diisi!') + .min(0.001, 'Sub Qty tidak boleh negatif!') + .typeError('Sub Qty harus berupa angka!'), + }); + +export const PurchaseRequestFormSchema: Yup.ObjectSchema = + Yup.object({ + supplier: Yup.object({ + value: Yup.number().min(1).required(), + label: Yup.string().required(), + }).nullable(), + supplier_id: Yup.number() + .required('Supplier wajib diisi!') + .min(1, 'Supplier wajib diisi!') + .typeError('Supplier wajib diisi!'), + credit_term: Yup.number() + .required('Termin kredit wajib diisi!') + .min(1, 'Termin kredit tidak boleh negatif!') + .typeError('Termin kredit harus berupa angka!'), + notes: Yup.string().nullable().default(null), + purchase_items: Yup.array() + .of(PurchaseItemObjectSchema) + .min(1, 'Minimal harus ada 1 item pembelian!') + .required('Item pembelian wajib diisi!') + .typeError('Item pembelian wajib diisi!'), + }); export const UpdatePurchaseRequestFormSchema = PurchaseRequestFormSchema; @@ -64,20 +108,8 @@ export type PurchaseRequestFormValues = Yup.InferType< typeof PurchaseRequestFormSchema >; -type PurchaseRequestFormData = { - supplier?: Supplier; - supplier_id?: number; - credit_term?: number; - notes?: string | null; - purchase_items?: (CreatePurchaseRequestPayload['purchase_items'][0] & { - warehouse?: Warehouse; - product?: Product; - product_warehouse?: ProductWarehouse; - })[]; -}; - export const getPurchaseRequestFormInitialValues = ( - initialValues?: PurchaseRequestFormData + initialValues?: Purchase ): PurchaseRequestFormValues => ({ supplier: initialValues?.supplier ? { @@ -85,45 +117,8 @@ export const getPurchaseRequestFormInitialValues = ( label: initialValues.supplier.name, } : null, - supplier_id: initialValues?.supplier_id ?? 0, - credit_term: initialValues?.credit_term ?? 1, - notes: initialValues?.notes ?? '', - purchase_items: initialValues?.purchase_items?.map( - (item: NonNullable[0]) => ({ - warehouse: item.warehouse - ? { - value: item.warehouse.id, - label: item.warehouse.name, - } - : null, - warehouse_id: item.warehouse_id, - product: item.product - ? { - value: item.product.id, - label: item.product.name, - } - : null, - product_id: item.product_id, - product_warehouse: item.product_warehouse - ? { - value: item.product_warehouse.id, - label: item.product_warehouse.product.name, - } - : null, - product_warehouse_id: item.product_warehouse_id || null, - sub_qty: item.sub_qty, - price: item.price, - }) - ) ?? [ - { - warehouse: null, - warehouse_id: 0, - product: null, - product_id: 0, - product_warehouse: null, - product_warehouse_id: null, - sub_qty: 0, - price: 0, - }, - ], + supplier_id: initialValues?.supplier?.id ?? 0, + credit_term: initialValues?.credit_term ?? '', + notes: initialValues?.notes ?? null, + purchase_items: [], }); diff --git a/src/components/pages/purchase/form/PurchaseRequestForm.tsx b/src/components/pages/purchase/form/PurchaseRequestForm.tsx index 21b0ca03..8da24397 100644 --- a/src/components/pages/purchase/form/PurchaseRequestForm.tsx +++ b/src/components/pages/purchase/form/PurchaseRequestForm.tsx @@ -142,19 +142,32 @@ const PurchaseRequestForm = ({ validateOnBlur: true, onSubmit: async (values) => { const payload: CreatePurchaseRequestPayload = { - supplier_id: values.supplier_id || 0, - credit_term: values.credit_term || 0, + supplier_id: + typeof values.supplier_id === 'string' + ? parseInt(values.supplier_id) || 0 + : values.supplier_id || 0, + credit_term: + typeof values.credit_term === 'string' + ? parseInt(values.credit_term) || 0 + : values.credit_term || 0, notes: values.notes || '', purchase_items: (values.purchase_items || []).map((item) => ({ - warehouse_id: item.warehouse_id || 0, - product_id: item.product_id || 0, - product_warehouse_id: item.product_warehouse_id || undefined, - sub_qty: item.sub_qty || 0, - total_qty: item.total_qty || 0, - price: - typeof item.price === 'number' - ? item.price - : parseFloat(item.price) || 0, + warehouse_id: + typeof item.warehouse_id === 'string' + ? parseInt(item.warehouse_id) || 0 + : item.warehouse_id || 0, + product_id: + typeof item.product_id === 'string' + ? parseInt(item.product_id) || 0 + : item.product_id || 0, + product_warehouse_id: + typeof item.product_warehouse_id === 'string' + ? parseInt(item.product_warehouse_id) || 0 + : item.product_warehouse_id || 0, + sub_qty: + typeof item.sub_qty === 'string' + ? parseFloat(item.sub_qty) || 0 + : item.sub_qty || 0, })), }; @@ -193,14 +206,12 @@ const PurchaseRequestForm = ({ ...(formik.values.purchase_items || []), { warehouse: null, - warehouse_id: 0, + warehouse_id: '', product: null, - product_id: 0, + product_id: '', product_warehouse: null, product_warehouse_id: null, - sub_qty: 0, - total_qty: 0, - price: 0, + sub_qty: '', }, ]; formik.setFieldValue('purchase_items', newPurchaseItems); @@ -285,12 +296,11 @@ const PurchaseRequestForm = ({ type='number' placeholder='Masukkan Supplier ID' /> - + {}} + onBlur={formik.handleBlur} + readOnly={type === 'detail'} + type='number' + placeholder='Pilih Area' + /> + + {}} + onBlur={formik.handleBlur} + readOnly={type === 'detail'} + type='number' + placeholder='Pilih Lokasi' + /> +
)} - Warehouse ID + Gudang * - Product ID + Item * - Product Warehouse ID - * - - - Sub Qty - * - - - Total Qty - * - - - Price + Jumlah * + Estimasi Harga + Satuan {type !== 'detail' && Action} @@ -410,7 +432,7 @@ const PurchaseRequestForm = ({ handlePurchaseItemChange( idx, @@ -420,28 +442,7 @@ const PurchaseRequestForm = ({ } onBlur={formik.handleBlur} type='number' - placeholder='Warehouse ID' - readOnly={type === 'detail'} - className={{ - wrapper: 'min-w-24', - }} - /> - - - - handlePurchaseItemChange( - idx, - 'product_id', - e.target.value - ) - } - onBlur={formik.handleBlur} - type='number' - placeholder='Product ID' + placeholder='Masukkan Warehouse ID' readOnly={type === 'detail'} className={{ wrapper: 'min-w-24', @@ -472,7 +473,7 @@ const PurchaseRequestForm = ({ handlePurchaseItemChange( idx, @@ -482,28 +483,7 @@ const PurchaseRequestForm = ({ } onBlur={formik.handleBlur} type='number' - placeholder='Sub Qty' - readOnly={type === 'detail'} - className={{ - wrapper: 'min-w-24', - }} - /> - - - - handlePurchaseItemChange( - idx, - 'total_qty', - e.target.value - ) - } - onBlur={formik.handleBlur} - type='number' - placeholder='Total Qty' + placeholder='Masukkan kuantitas' readOnly={type === 'detail'} className={{ wrapper: 'min-w-24', @@ -514,7 +494,6 @@ const PurchaseRequestForm = ({ handlePurchaseItemChange( idx, @@ -524,8 +503,22 @@ const PurchaseRequestForm = ({ } onBlur={formik.handleBlur} type='number' - placeholder='Price' - readOnly={type === 'detail'} + className={{ + wrapper: 'min-w-24', + }} + disabled={true} + readOnly={true} + inputPrefix={'Rp'} + /> + + +