diff --git a/src/components/pages/marketing/form/MarketingForm.schema.ts b/src/components/pages/marketing/form/MarketingForm.schema.ts index d81cdb9c..b09129c3 100644 --- a/src/components/pages/marketing/form/MarketingForm.schema.ts +++ b/src/components/pages/marketing/form/MarketingForm.schema.ts @@ -11,6 +11,13 @@ import { type MarketingSchemaType = { customer_id: number | undefined; sales_person_id: number | undefined; + sales_person: + | { + value: number; + label: string; + } + | undefined + | null; customer: | { value: number; @@ -33,7 +40,11 @@ type DeliveryOrderSchemaType = { export const SalesOrderSchema: Yup.ObjectSchema = Yup.object({ customer_id: Yup.number().required('Customer wajib diisi!'), - sales_person_id: Yup.number().required('Sales Person wajib diisi!'), + sales_person_id: Yup.number().required('Sales wajib diisi!'), + sales_person: Yup.object({ + value: Yup.number().required(), + label: Yup.string().required(), + }).nullable(), customer: Yup.object({ value: Yup.number().required(), label: Yup.string().required(), diff --git a/src/components/pages/marketing/form/MarketingForm.tsx b/src/components/pages/marketing/form/MarketingForm.tsx index 2fbca835..be4367cb 100644 --- a/src/components/pages/marketing/form/MarketingForm.tsx +++ b/src/components/pages/marketing/form/MarketingForm.tsx @@ -50,6 +50,8 @@ import { DeliveryOrderProductFormValues } from '@/components/pages/marketing/for import RequirePermission from '@/components/helper/RequirePermission'; import AlertErrorList from '@/components/helper/form/FormErrors'; import { useFormikErrorList } from '@/services/hooks/useFormikErrorList'; +import { CreatedUser } from '@/types/api/api-general'; +import { UserApi } from '@/services/api/user'; const MemoizedSalesOrderProductTable = memo(SalesOrderProductTable); const MemoizedSalesOrderProductForm = memo(SalesOrderProductForm); @@ -244,7 +246,15 @@ const MarketingForm = ({ const { options: customerOptions, isLoadingOptions: isLoadingCustomerOptions, + setInputValue: setInputCustomerValue, + loadMore: loadMoreCustomer, } = useSelect(CustomerApi.basePath, 'id', 'name'); + const { + options: salesOptions, + isLoadingOptions: isLoadingSalesOptions, + setInputValue: setInputSalesValue, + loadMore: loadMoreSales, + } = useSelect(UserApi.basePath, 'id', 'name'); // ================== SETUP FORMIK ================== const formikInitialValues = useMemo< @@ -255,6 +265,12 @@ const MarketingForm = ({ notes: initialValues?.notes || undefined, customer_id: initialValues?.customer?.id || undefined, sales_person_id: initialValues?.sales_person?.id || 1, + sales_person: initialValues?.sales_person + ? { + value: initialValues.sales_person.id, + label: initialValues.sales_person.name, + } + : null, customer: initialValues?.customer ? { value: initialValues.customer.id, @@ -443,6 +459,13 @@ const MarketingForm = ({ }, [] ); + const handleChangeSalesPerson = useCallback( + (val: OptionType | OptionType[] | null) => { + formik.setFieldValue('sales_person_id', (val as OptionType)?.value); + formik.setFieldValue('sales_person', val as OptionType); + }, + [] + ); const handleDelete = useCallback(() => { deleteModal.openModal(); }, [deleteModal]); @@ -580,6 +603,7 @@ const MarketingForm = ({ className={{ wrapper: 'bg-white w-full', }} + variant='bordered' >
- -
+
+ + +
+
Total Penjualan {formatCurrency(grandTotal)}{' '} 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 25a20982..da3b2fee 100644 --- a/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx +++ b/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx @@ -18,6 +18,11 @@ import * as Yup from 'yup'; import { isResponseSuccess } from '@/lib/api-helper'; import AlertErrorList from '@/components/helper/form/FormErrors'; import { useFormikErrorList } from '@/services/hooks/useFormikErrorList'; +import useSWR from 'swr'; +import { ProductApi } from '@/services/api/master-data'; + +const roundWeight = (value: number) => Number(value.toFixed(2)); +const roundPrice = (value: number) => Math.round(value); const DeliveryOrderProductForm = ({ formState, @@ -43,6 +48,17 @@ const DeliveryOrderProductForm = ({ ); const [currentInput, setCurrentInput] = useState(''); + // ============ Fetch Data ============ + const { data: productData } = useSWR( + selectedProduct?.value + ? ProductApi.basePath + '/' + selectedProduct?.value + : null, + () => + selectedProduct?.value + ? ProductApi.getSingle(Number(selectedProduct?.value)) + : undefined + ); + const salesOrder = salesOrders.find( (item) => item.id === initialValues?.marketing_product_id ); @@ -113,22 +129,60 @@ const DeliveryOrderProductForm = ({ const handleBlurField = (field: string) => { setCurrentInput(field); - const { qty, unit_price, total_price, avg_weight, total_weight } = - formik.values; - if (field === 'unit_price' || field === 'total_price' || field === 'qty') { - if (qty && unit_price && (field === 'unit_price' || field === 'qty')) { - formik.setFieldValue('total_price', Number(qty) * Number(unit_price)); - } else if (qty && total_price && field === 'total_price') { - formik.setFieldValue('unit_price', Number(total_price) / Number(qty)); + const qty = Number(formik.values.qty || 0); + const avgWeight = Number(formik.values.avg_weight || 0); + const totalWeight = Number(formik.values.total_weight || 0); + const unitPrice = Number(formik.values.unit_price || 0); + const totalPrice = Number(formik.values.total_price || 0); + + if (qty <= 0) return; + + switch (field) { + // ===== SOURCE FIELDS ===== + case 'qty': { + if (avgWeight > 0) { + formik.setFieldValue('total_weight', roundWeight(qty * avgWeight)); + } + + if (unitPrice > 0) { + formik.setFieldValue('total_price', roundPrice(qty * unitPrice)); + } + break; } - } - if (field === 'avg_weight' || field === 'total_weight' || field === 'qty') { - if (qty && avg_weight && (field === 'avg_weight' || field === 'qty')) { - formik.setFieldValue('total_weight', Number(qty) * Number(avg_weight)); - } else if (qty && total_weight && field === 'total_weight') { - formik.setFieldValue('avg_weight', Number(total_weight) / Number(qty)); + case 'avg_weight': { + if (avgWeight > 0) { + const tw = roundWeight(qty * avgWeight); + formik.setFieldValue('total_weight', tw); + + if (unitPrice > 0) { + formik.setFieldValue('total_price', roundPrice(qty * unitPrice)); + } + } + break; + } + + case 'unit_price': { + if (unitPrice > 0) { + formik.setFieldValue('total_price', roundPrice(qty * unitPrice)); + } + break; + } + + // ===== TOTAL EDITABLE ===== + case 'total_weight': { + if (totalWeight > 0) { + formik.setFieldValue('avg_weight', roundWeight(totalWeight / qty)); + } + break; + } + + case 'total_price': { + if (totalPrice > 0) { + formik.setFieldValue('unit_price', roundPrice(totalPrice / qty)); + } + break; } } }; @@ -183,7 +237,7 @@ const DeliveryOrderProductForm = ({
)} -
+
- +
+
+
+ + {isResponseSuccess(productData) + ? productData?.data?.uom.name + : ''} + +
+ } bottomLabel={ formik.values.marketing_product_id ? 'Stok dijual: ' + salesOrders?.find( (item) => item.id === formik.values.marketing_product_id - )?.qty + )?.qty + + ' ' + + (isResponseSuccess(productData) + ? productData?.data?.uom.name + : '') : '' } /> -
-
-
- { - formik.handleChange(e); - setCurrentInput(e.target.name); - }} - onBlur={() => handleBlurField('avg_weight')} - isError={Boolean(formik.errors.avg_weight)} - errorMessage={formik.errors.avg_weight} - placeholder='Masukan Bobot Rata-rata' - /> - - + { + formik.handleChange(e); + setCurrentInput(e.target.name); + }} + onBlur={() => handleBlurField('avg_weight')} + isError={Boolean(formik.errors.avg_weight)} + errorMessage={formik.errors.avg_weight} + placeholder='Masukan Bobot Rata-rata' + /> Number(value.toFixed(2)); +const roundPrice = (value: number) => Math.round(value); const SalesOrderProductForm = ({ initialValues, @@ -39,6 +43,19 @@ const SalesOrderProductForm = ({ }) => { const [formErrorMessage, setFormErrorMessage] = useState(''); const [currentInput, setCurrentInput] = useState(''); + const [selectedProductWarehouse, setSelectedProductWarehouse] = + useState(null); + + // ============ Fetch Data ============ + const { data: productData } = useSWR( + selectedProductWarehouse?.product_id + ? ProductApi.basePath + '/' + selectedProductWarehouse?.product_id + : null, + () => + selectedProductWarehouse?.product_id + ? ProductApi.getSingle(selectedProductWarehouse?.product_id) + : undefined + ); // ============ Formik ============ const formik = useFormik({ @@ -69,17 +86,21 @@ const SalesOrderProductForm = ({ const { options: kandangSourceOptions, isLoadingOptions: isLoadingKandangSourceOptions, + setInputValue: setKandangInputValue, + loadMore: loadMoreKandang, } = useSelect(WarehouseApi.basePath, 'id', 'name'); const { options: warehouseSourceOptions, rawData: warehouseSourceRawData, isLoadingOptions: isLoadingWarehouseSourceOptions, + setInputValue: setWarehouseInputValue, + loadMore: loadMoreWarehouse, } = useSelect( ProductWarehouseApi.basePath, 'id', 'product.name', - 'search', + '', { warehouse_id: formik.values.kandang_id?.toString() ?? '', } @@ -112,6 +133,7 @@ const SalesOrderProductForm = ({ const productWarehouse = warehouseSourceRawData?.data.find( (item: ProductWarehouse) => item.id === newId ); + setSelectedProductWarehouse(productWarehouse || null); formik.setFieldValue('qty', productWarehouse?.quantity); handleBlurField('qty'); } else { @@ -139,34 +161,60 @@ const SalesOrderProductForm = ({ const handleBlurField = (field: string) => { setCurrentInput(field); - const { qty, unit_price, total_price, avg_weight, total_weight } = - formik.values; - if (field === 'unit_price' || field === 'total_price' || field === 'qty') { - if (qty && unit_price && (field === 'unit_price' || field === 'qty')) { - formik.setFieldValue( - 'total_price', - (qty as number) * (unit_price as number) - ); - } else if (qty && total_price && field === 'total_price') { - formik.setFieldValue( - 'unit_price', - (total_price as number) / (qty as number) - ); + const qty = Number(formik.values.qty || 0); + const avgWeight = Number(formik.values.avg_weight || 0); + const totalWeight = Number(formik.values.total_weight || 0); + const unitPrice = Number(formik.values.unit_price || 0); + const totalPrice = Number(formik.values.total_price || 0); + + if (qty <= 0) return; + + switch (field) { + // ===== SOURCE FIELDS ===== + case 'qty': { + if (avgWeight > 0) { + formik.setFieldValue('total_weight', roundWeight(qty * avgWeight)); + } + + if (unitPrice > 0) { + formik.setFieldValue('total_price', roundPrice(qty * unitPrice)); + } + break; } - } - if (field === 'avg_weight' || field === 'total_weight' || field === 'qty') { - if (qty && avg_weight && (field === 'avg_weight' || field === 'qty')) { - formik.setFieldValue( - 'total_weight', - (qty as number) * (avg_weight as number) - ); - } else if (qty && total_weight && field === 'total_weight') { - formik.setFieldValue( - 'avg_weight', - (total_weight as number) / (qty as number) - ); + case 'avg_weight': { + if (avgWeight > 0) { + const tw = roundWeight(qty * avgWeight); + formik.setFieldValue('total_weight', tw); + + if (unitPrice > 0) { + formik.setFieldValue('total_price', roundPrice(qty * unitPrice)); + } + } + break; + } + + case 'unit_price': { + if (unitPrice > 0) { + formik.setFieldValue('total_price', roundPrice(qty * unitPrice)); + } + break; + } + + // ===== TOTAL EDITABLE ===== + case 'total_weight': { + if (totalWeight > 0) { + formik.setFieldValue('avg_weight', roundWeight(totalWeight / qty)); + } + break; + } + + case 'total_price': { + if (totalPrice > 0) { + formik.setFieldValue('unit_price', roundPrice(totalPrice / qty)); + } + break; } } }; @@ -188,7 +236,7 @@ const SalesOrderProductForm = ({
)} -
+
+
+
+
+ + {isResponseSuccess(productData) + ? productData?.data?.uom.name + : ''} + +
+ } bottomLabel={ isResponseSuccess(warehouseSourceRawData) && formik.values.product_warehouse_id @@ -264,32 +328,13 @@ const SalesOrderProductForm = ({ (item) => item.id === formik.values.product_warehouse_id )?.quantity ?? 0 )} ${ - warehouseSourceRawData?.data?.find( - (item) => item.id === formik.values.product_warehouse_id - )?.product?.uom?.name ?? '' + isResponseSuccess(productData) + ? productData?.data?.uom.name + : '' }` : '' } /> -
-
-
- { - formik.handleChange(e); - setCurrentInput(e.target.name); - }} - onBlur={() => handleBlurField('avg_weight')} - isError={ - formik.touched.avg_weight && Boolean(formik.errors.avg_weight) - } - errorMessage={formik.errors.avg_weight} - placeholder='Masukan Bobot Rata-rata' - /> + { + formik.handleChange(e); + setCurrentInput(e.target.name); + }} + onBlur={() => handleBlurField('avg_weight')} + isError={ + formik.touched.avg_weight && Boolean(formik.errors.avg_weight) + } + errorMessage={formik.errors.avg_weight} + placeholder='Masukan Bobot Rata-rata' + /> { todayRecordings.forEach((recording) => { const recordingDate = recording.record_datetime?.split('T')[0]; if (recordingDate === today) { - recordedIds.add(recording.project_flock.project_flock_kandang_id); + recordedIds.add(recording.project_flock?.project_flock_kandang_id); } });