diff --git a/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.schema.ts b/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.schema.ts index c7cb4e9f..bbc6986d 100644 --- a/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.schema.ts +++ b/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.schema.ts @@ -44,13 +44,29 @@ export const DeliveryOrderProductSchema: Yup.ObjectSchema + value === '' || value === null ? 0 : Number(value) + ) .min(0, 'Total Bobot wajib diisi!') + .test( + 'is-greater-than-zero', + 'Total Bobot harus lebih dari 0!', + (value) => value !== undefined && value > 0 + ) .required('Total Bobot wajib diisi!'), qty: Yup.number() .min(1, 'Kuantitas wajib diisi!') .required('Kuantitas wajib diisi!'), avg_weight: Yup.number() + .transform((value) => + value === '' || value === null ? 0 : Number(value) + ) .min(0, 'Avg. Bobot wajib diisi!') + .test( + 'is-greater-than-zero', + 'Avg. Bobot harus lebih dari 0!', + (value) => value !== undefined && value > 0 + ) .required('Avg. Bobot wajib diisi!'), total_price: Yup.number() .min(1, 'Total Penjualan wajib diisi!') diff --git a/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx b/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx index aae37d8e..c10976eb 100644 --- a/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx +++ b/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx @@ -236,6 +236,25 @@ const DeliveryOrderProductForm = ({ }); }; + // Handler untuk onChange - auto calculation real-time untuk field yang mempengaruhi total_price (total_peti, weight_per_convertion, price_per_convertion, sisa_berat, price_sisa_berat, price_per_qty, qty) + const handleFieldChange = ( + field: string, + value: number | string, + callback?: () => void + ) => { + formik.setFieldValue(field, value); + + setTimeout(() => { + handleMarketingCalculation(field, { + values: { ...formik.values, [field]: value }, + setFieldValue: formik.setFieldValue, + hasSisaBerat, + }); + }, 0); + + if (callback) callback(); + }; + // Handler khusus untuk toggle sisa berat - langsung pakai nilai baru const handleSisaBeratToggle = (newHasSisaBerat: boolean) => { setHasSisaBerat(newHasSisaBerat); @@ -520,13 +539,11 @@ const DeliveryOrderProductForm = ({ } per ${formik.values.convertion_unit?.value}`} value={formik.values.weight_per_convertion ?? ''} onChange={(e) => { - formik.setFieldValue( - 'weight_per_convertion', - Number(e.target.value) + const value = Number(e.target.value); + handleFieldChange('weight_per_convertion', value, () => + setCurrentInput(e.target.name) ); - setCurrentInput(e.target.name); }} - onBlur={() => handleBlurField('weight_per_convertion')} /> @@ -564,10 +581,11 @@ const DeliveryOrderProductForm = ({ name='total_peti' value={formik.values.total_peti ?? undefined} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('total_peti', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('total_peti')} isError={ formik.touched.total_peti && Boolean(formik.errors.total_peti) } @@ -592,10 +610,11 @@ const DeliveryOrderProductForm = ({ name='avg_weight' value={formik.values.avg_weight} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('avg_weight', value, () => + setCurrentInput('avg_weight') + ); }} - onBlur={() => handleBlurField('avg_weight')} isError={ formik.touched.avg_weight && Boolean(formik.errors.avg_weight) @@ -613,10 +632,11 @@ const DeliveryOrderProductForm = ({ name='total_weight' value={formik.values.total_weight} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('total_weight', value, () => + setCurrentInput('total_weight') + ); }} - onBlur={() => handleBlurField('total_weight')} isError={ formik.touched.total_weight && Boolean(formik.errors.total_weight) @@ -638,10 +658,11 @@ const DeliveryOrderProductForm = ({ name='qty' value={formik.values.qty} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('qty', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('qty')} isError={Boolean(formik.errors.qty)} errorMessage={formik.errors.qty} placeholder='Masukan Kuantitas' @@ -677,10 +698,11 @@ const DeliveryOrderProductForm = ({ name='price_per_convertion' value={formik.values.price_per_convertion ?? undefined} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('price_per_convertion', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('price_per_convertion')} isError={ formik.touched.price_per_convertion && Boolean(formik.errors.price_per_convertion) @@ -699,10 +721,11 @@ const DeliveryOrderProductForm = ({ name='price_per_qty' value={formik.values.price_per_qty ?? undefined} onChange={(e) => { - formik.setFieldValue('price_per_qty', Number(e.target.value)); - setCurrentInput('price_per_qty'); + const value = Number(e.target.value); + handleFieldChange('price_per_qty', value, () => + setCurrentInput('price_per_qty') + ); }} - onBlur={() => handleBlurField('price_per_qty')} isError={ formik.touched.price_per_qty && Boolean(formik.errors.price_per_qty) @@ -721,10 +744,11 @@ const DeliveryOrderProductForm = ({ name='unit_price' value={formik.values.unit_price} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('unit_price', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('unit_price')} isError={Boolean(formik.errors.unit_price)} errorMessage={formik.errors.unit_price} placeholder='Masukan Harga Satuan' @@ -760,10 +784,11 @@ const DeliveryOrderProductForm = ({ name='sisa_berat' value={formik.values.sisa_berat ?? undefined} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('sisa_berat', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('sisa_berat')} isError={ formik.touched.sisa_berat && Boolean(formik.errors.sisa_berat) } @@ -776,10 +801,11 @@ const DeliveryOrderProductForm = ({ name='price_sisa_berat' value={formik.values.price_sisa_berat ?? undefined} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('price_sisa_berat', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('price_sisa_berat')} isError={ formik.touched.price_sisa_berat && Boolean(formik.errors.price_sisa_berat) @@ -797,10 +823,11 @@ const DeliveryOrderProductForm = ({ name='total_price' value={formik.values.total_price} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('total_price', value, () => + setCurrentInput('total_price') + ); }} - onBlur={() => handleBlurField('total_price')} isError={ formik.touched.total_price && Boolean(formik.errors.total_price) } diff --git a/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema.ts b/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema.ts index 8bd5bf72..349e0d77 100644 --- a/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema.ts +++ b/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema.ts @@ -70,13 +70,29 @@ export const SalesOrderProductSchema: Yup.ObjectSchema + value === '' || value === null ? 0 : Number(value) + ) + .min(0, 'Total Bobot tidak boleh negatif!') + .test( + 'is-greater-than-zero', + 'Total Bobot harus lebih dari 0!', + (value) => value !== undefined && value > 0 + ) .required('Total Bobot wajib diisi!'), qty: Yup.number() .min(1, 'Kuantitas wajib diisi!') .required('Kuantitas wajib diisi!'), avg_weight: Yup.number() - .min(1, 'Avg. Bobot wajib diisi!') + .transform((value) => + value === '' || value === null ? 0 : Number(value) + ) + .min(0, 'Avg. Bobot wajib diisi!') + .test( + 'is-greater-than-zero', + 'Avg. Bobot harus lebih dari 0!', + (value) => value !== undefined && value > 0 + ) .required('Avg. Bobot wajib diisi!'), total_price: Yup.number() .min(1, 'Total Penjualan wajib diisi!') diff --git a/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx b/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx index 70965071..8da873e5 100644 --- a/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx +++ b/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx @@ -250,6 +250,25 @@ const SalesOrderProductForm = ({ }); }; + // Handler untuk onChange - auto calculation real-time untuk field yang mempengaruhi total_price (total_peti, weight_per_convertion, price_per_convertion, sisa_berat, price_sisa_berat, price_per_qty, qty) + const handleFieldChange = ( + field: string, + value: number | string, + callback?: () => void + ) => { + formik.setFieldValue(field, value); + + setTimeout(() => { + handleMarketingCalculation(field, { + values: { ...formik.values, [field]: value }, + setFieldValue: formik.setFieldValue, + hasSisaBerat, + }); + }, 0); + + if (callback) callback(); + }; + // Handler khusus untuk toggle sisa berat - langsung pakai nilai baru const handleSisaBeratToggle = (newHasSisaBerat: boolean) => { setHasSisaBerat(newHasSisaBerat); @@ -475,13 +494,11 @@ const SalesOrderProductForm = ({ } per ${formik.values.convertion_unit?.value}`} value={formik.values.weight_per_convertion ?? ''} onChange={(e) => { - formik.setFieldValue( - 'weight_per_convertion', - Number(e.target.value) + const value = Number(e.target.value); + handleFieldChange('weight_per_convertion', value, () => + setCurrentInput(e.target.name) ); - setCurrentInput(e.target.name); }} - onBlur={() => handleBlurField('weight_per_convertion')} /> @@ -519,10 +536,11 @@ const SalesOrderProductForm = ({ name='total_peti' value={formik.values.total_peti ?? undefined} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('total_peti', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('total_peti')} isError={ formik.touched.total_peti && Boolean(formik.errors.total_peti) } @@ -547,10 +565,11 @@ const SalesOrderProductForm = ({ name='avg_weight' value={formik.values.avg_weight} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('avg_weight', value, () => + setCurrentInput('avg_weight') + ); }} - onBlur={() => handleBlurField('avg_weight')} isError={ formik.touched.avg_weight && Boolean(formik.errors.avg_weight) @@ -568,10 +587,11 @@ const SalesOrderProductForm = ({ name='total_weight' value={formik.values.total_weight} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('total_weight', value, () => + setCurrentInput('total_weight') + ); }} - onBlur={() => handleBlurField('total_weight')} isError={ formik.touched.total_weight && Boolean(formik.errors.total_weight) @@ -593,10 +613,11 @@ const SalesOrderProductForm = ({ name='qty' value={formik.values.qty} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('qty', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('qty')} isError={formik.touched.qty && Boolean(formik.errors.qty)} errorMessage={formik.errors.qty} placeholder='Masukan Kuantitas' @@ -630,10 +651,11 @@ const SalesOrderProductForm = ({ name='price_per_convertion' value={formik.values.price_per_convertion ?? undefined} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('price_per_convertion', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('price_per_convertion')} isError={ formik.touched.price_per_convertion && Boolean(formik.errors.price_per_convertion) @@ -652,10 +674,11 @@ const SalesOrderProductForm = ({ name='price_per_qty' value={formik.values.price_per_qty ?? undefined} onChange={(e) => { - formik.setFieldValue('price_per_qty', Number(e.target.value)); - setCurrentInput('price_per_qty'); + const value = Number(e.target.value); + handleFieldChange('price_per_qty', value, () => + setCurrentInput('price_per_qty') + ); }} - onBlur={() => handleBlurField('price_per_qty')} isError={ formik.touched.price_per_qty && Boolean(formik.errors.price_per_qty) @@ -674,10 +697,11 @@ const SalesOrderProductForm = ({ name='unit_price' value={formik.values.unit_price} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('unit_price', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('unit_price')} isError={ formik.touched.unit_price && Boolean(formik.errors.unit_price) } @@ -715,10 +739,11 @@ const SalesOrderProductForm = ({ name='sisa_berat' value={formik.values.sisa_berat ?? undefined} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('sisa_berat', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('sisa_berat')} isError={ formik.touched.sisa_berat && Boolean(formik.errors.sisa_berat) } @@ -731,10 +756,11 @@ const SalesOrderProductForm = ({ name='price_sisa_berat' value={formik.values.price_sisa_berat ?? undefined} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('price_sisa_berat', value, () => + setCurrentInput(e.target.name) + ); }} - onBlur={() => handleBlurField('price_sisa_berat')} isError={ formik.touched.price_sisa_berat && Boolean(formik.errors.price_sisa_berat) @@ -752,10 +778,11 @@ const SalesOrderProductForm = ({ name='total_price' value={formik.values.total_price} onChange={(e) => { - formik.handleChange(e); - setCurrentInput(e.target.name); + const value = Number(e.target.value); + handleFieldChange('total_price', value, () => + setCurrentInput('total_price') + ); }} - onBlur={() => handleBlurField('total_price')} isError={ formik.touched.total_price && Boolean(formik.errors.total_price) }