diff --git a/src/components/pages/inventory/movement/form/MovementForm.schema.ts b/src/components/pages/inventory/movement/form/MovementForm.schema.ts index 11404782..2cc5d910 100644 --- a/src/components/pages/inventory/movement/form/MovementForm.schema.ts +++ b/src/components/pages/inventory/movement/form/MovementForm.schema.ts @@ -11,7 +11,7 @@ export type ProductSchema = { }; export type DeliverySchema = { - delivery_cost: number; + delivery_cost?: number | undefined; delivery_cost_per_item?: number | undefined; document?: File | string | null; driver_name: string; @@ -57,13 +57,35 @@ const DeliveryProductObjectSchema = Yup.object({ const DeliveryObjectSchema: Yup.ObjectSchema = Yup.object({ delivery_cost: Yup.number() - .required('Biaya pengiriman wajib diisi!') + .transform((value) => (isNaN(value) || value === 0 ? undefined : value)) .min(1, 'Biaya minimal 1!') - .typeError('Biaya harus berupa angka!'), + .typeError('Biaya harus berupa angka!') + .test( + 'one-of-cost-fields', + 'Biaya pengiriman atau biaya per item wajib diisi!', + function (value) { + const { delivery_cost_per_item } = this.parent; + return ( + (value !== undefined && value > 0) || + (delivery_cost_per_item !== undefined && delivery_cost_per_item > 0) + ); + } + ), delivery_cost_per_item: Yup.number() - .transform((value) => (isNaN(value) ? undefined : value)) + .transform((value) => (isNaN(value) || value === 0 ? undefined : value)) .min(1, 'Biaya per item minimal 1!') - .typeError('Biaya per item harus berupa angka!'), + .typeError('Biaya per item harus berupa angka!') + .test( + 'one-of-cost-fields', + 'Biaya pengiriman atau biaya per item wajib diisi!', + function (value) { + const { delivery_cost } = this.parent; + return ( + (value !== undefined && value > 0) || + (delivery_cost !== undefined && delivery_cost > 0) + ); + } + ), document_index: Yup.number().optional(), document: Yup.mixed() .nullable() diff --git a/src/components/pages/inventory/movement/form/MovementForm.tsx b/src/components/pages/inventory/movement/form/MovementForm.tsx index d29ae851..2ec06e66 100644 --- a/src/components/pages/inventory/movement/form/MovementForm.tsx +++ b/src/components/pages/inventory/movement/form/MovementForm.tsx @@ -83,7 +83,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { } return { - delivery_cost: d.delivery_cost, + delivery_cost: d.delivery_cost ?? 0, delivery_cost_per_item: d.delivery_cost_per_item ?? 0, document_index: documentIndex, driver_name: d.driver_name, @@ -163,8 +163,8 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { formik.setFieldValue('deliveries', [ ...(formik.values.deliveries || []), { - delivery_cost: 0, - delivery_cost_per_item: 0, + delivery_cost: undefined, + delivery_cost_per_item: undefined, document: null, driver_name: '', vehicle_plate: '', @@ -324,21 +324,86 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { ? suppliers?.data.map((s) => ({ value: s.id, label: s.name })) : []; + // Handle cost calculation when delivery_cost changes + const handleDeliveryCostChange = useCallback( + (idx: number, value: string) => { + const numValue = parseFloat(value) || 0; + formik.setFieldValue(`deliveries.${idx}.delivery_cost`, numValue); + + const delivery = formik.values.deliveries?.[idx]; + if (delivery) { + const productQty = delivery.products.reduce( + (sum, p) => sum + p.product_qty, + 0 + ); + if (productQty > 0 && numValue > 0) { + const perItem = numValue / productQty; + formik.setFieldValue( + `deliveries.${idx}.delivery_cost_per_item`, + perItem + ); + } else if (numValue === 0) { + formik.setFieldValue(`deliveries.${idx}.delivery_cost_per_item`, 0); + } + } + }, + [formik] + ); + + // Handle cost calculation when delivery_cost_per_item changes + const handleDeliveryCostPerItemChange = useCallback( + (idx: number, value: string) => { + const numValue = parseFloat(value) || 0; + formik.setFieldValue(`deliveries.${idx}.delivery_cost_per_item`, numValue); + + const delivery = formik.values.deliveries?.[idx]; + if (delivery) { + const productQty = delivery.products.reduce( + (sum, p) => sum + p.product_qty, + 0 + ); + if (productQty > 0 && numValue > 0) { + const totalCost = numValue * productQty; + formik.setFieldValue(`deliveries.${idx}.delivery_cost`, totalCost); + } else if (numValue === 0) { + formik.setFieldValue(`deliveries.${idx}.delivery_cost`, 0); + } + } + }, + [formik] + ); + + // Auto-recalculate when product quantity changes useEffect(() => { formik.values.deliveries?.forEach((delivery, idx) => { const productQty = delivery.products.reduce( (sum, p) => sum + p.product_qty, 0 ); - if (productQty && delivery.delivery_cost) { + + // If delivery_cost is set, recalculate delivery_cost_per_item + if (delivery.delivery_cost && delivery.delivery_cost > 0 && productQty > 0) { const perItem = delivery.delivery_cost / productQty; - formik.setFieldValue( - `deliveries.${idx}.delivery_cost_per_item`, - perItem - ); + if (Math.abs((delivery.delivery_cost_per_item || 0) - perItem) > 0.01) { + formik.setFieldValue( + `deliveries.${idx}.delivery_cost_per_item`, + perItem + ); + } + } + // If delivery_cost_per_item is set, recalculate delivery_cost + else if ( + delivery.delivery_cost_per_item && + delivery.delivery_cost_per_item > 0 && + productQty > 0 + ) { + const totalCost = delivery.delivery_cost_per_item * productQty; + if (Math.abs((delivery.delivery_cost || 0) - totalCost) > 0.01) { + formik.setFieldValue(`deliveries.${idx}.delivery_cost`, totalCost); + } } }); - }, [formik.values.deliveries]); + }, [formik.values.deliveries?.map(d => d.products.reduce((sum, p) => sum + p.product_qty, 0)).join(',')]); useEffect(() => { if ( @@ -1039,8 +1104,10 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { required type='number' name={`deliveries.${idx}.delivery_cost`} - value={delivery.delivery_cost} - onChange={formik.handleChange} + value={delivery.delivery_cost || ''} + onChange={(e) => + handleDeliveryCostChange(idx, e.target.value) + } onBlur={formik.handleBlur} {...isRepeaterInputError( 'deliveries', @@ -1052,17 +1119,20 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { + handleDeliveryCostPerItemChange(idx, e.target.value) } - readOnly - className={{ - input: 'bg-base-200', - }} + onBlur={formik.handleBlur} + {...isRepeaterInputError( + 'deliveries', + 'delivery_cost_per_item', + idx + )} + readOnly={type === 'detail'} />