diff --git a/src/app/marketing/add/delivery-orders/layout.tsx b/src/app/marketing/add/delivery-orders/layout.tsx
deleted file mode 100644
index 7220dfa1..00000000
--- a/src/app/marketing/add/delivery-orders/layout.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import SuspenseHelper from '@/components/helper/SuspenseHelper';
-
-const Layout = ({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) => {
- return {children};
-};
-
-export default Layout;
diff --git a/src/app/marketing/add/delivery-orders/page.tsx b/src/app/marketing/add/delivery-orders/page.tsx
deleted file mode 100644
index 4d92acda..00000000
--- a/src/app/marketing/add/delivery-orders/page.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-'use client';
-
-import MarketingForm from '@/components/pages/marketing/form/MarketingForm';
-import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-import { MarketingApi } from '@/services/api/marketing/marketing';
-import { useRouter, useSearchParams } from 'next/navigation';
-import toast from 'react-hot-toast';
-import useSWR from 'swr';
-
-const EditMarketingDelivery = () => {
- const router = useRouter();
- const searchParams = useSearchParams();
-
- const soId = searchParams.get('marketingId');
-
- const {
- data: marketing,
- isLoading: isLoading,
- mutate: refreshMarketing,
- } = useSWR(`get-so-${soId}`, () =>
- MarketingApi.getSingle(soId ? parseInt(soId) : 0)
- );
-
- if (!soId) {
- router.back();
-
- return (
-
-
-
- );
- }
-
- if (!isLoading && (!marketing || isResponseError(marketing))) {
- router.replace('/404');
- return;
- }
-
- return (
-
- {isLoading && }
- {!isLoading && isResponseSuccess(marketing) && (
- {
- refreshMarketing();
- }}
- />
- )}
-
- );
-};
-export default EditMarketingDelivery;
diff --git a/src/app/marketing/add/sales-orders/page.tsx b/src/app/marketing/add/sales-orders/page.tsx
deleted file mode 100644
index 9e33d304..00000000
--- a/src/app/marketing/add/sales-orders/page.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import MarketingForm from '@/components/pages/marketing/form/MarketingForm';
-
-const AddSalesOrder = () => {
- return (
-
-
-
- );
-};
-
-export default AddSalesOrder;
diff --git a/src/app/marketing/detail/delivery-orders/edit/layout.tsx b/src/app/marketing/detail/delivery-orders/edit/layout.tsx
deleted file mode 100644
index 7220dfa1..00000000
--- a/src/app/marketing/detail/delivery-orders/edit/layout.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import SuspenseHelper from '@/components/helper/SuspenseHelper';
-
-const Layout = ({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) => {
- return {children};
-};
-
-export default Layout;
diff --git a/src/app/marketing/detail/delivery-orders/edit/page.tsx b/src/app/marketing/detail/delivery-orders/edit/page.tsx
deleted file mode 100644
index 32625026..00000000
--- a/src/app/marketing/detail/delivery-orders/edit/page.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-'use client';
-
-import MarketingForm from '@/components/pages/marketing/form/MarketingForm';
-import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-import { MarketingApi } from '@/services/api/marketing/marketing';
-import { useRouter, useSearchParams } from 'next/navigation';
-import toast from 'react-hot-toast';
-import useSWR from 'swr';
-
-const EditMarketingDelivery = () => {
- const router = useRouter();
- const searchParams = useSearchParams();
-
- const soId = searchParams.get('marketingId');
-
- const {
- data: marketing,
- isLoading: isLoading,
- mutate: refreshMarketing,
- } = useSWR(`get-so-${soId}`, () =>
- MarketingApi.getSingle(soId ? parseInt(soId) : 0)
- );
-
- if (!soId) {
- router.back();
-
- return (
-
-
-
- );
- }
-
- if (!isLoading && (!marketing || isResponseError(marketing))) {
- router.replace('/404');
- return;
- }
-
- if (
- isResponseSuccess(marketing) &&
- marketing.data.latest_approval.step_number != 3
- ) {
- toast.error('Data Marketing perlu dilakukan approval terlebih dahulu!');
- router.back();
- }
-
- return (
-
- {isLoading && }
- {!isLoading && isResponseSuccess(marketing) && (
- {
- refreshMarketing();
- }}
- />
- )}
-
- );
-};
-export default EditMarketingDelivery;
diff --git a/src/app/marketing/detail/layout.tsx b/src/app/marketing/detail/layout.tsx
deleted file mode 100644
index 7220dfa1..00000000
--- a/src/app/marketing/detail/layout.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import SuspenseHelper from '@/components/helper/SuspenseHelper';
-
-const Layout = ({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) => {
- return {children};
-};
-
-export default Layout;
diff --git a/src/app/marketing/detail/page.tsx b/src/app/marketing/detail/page.tsx
deleted file mode 100644
index 902251e8..00000000
--- a/src/app/marketing/detail/page.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-'use client';
-
-import MarketingDetail from '@/components/pages/marketing/detail/MarketingDetail';
-import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-import { MarketingApi } from '@/services/api/marketing/marketing';
-import { useRouter, useSearchParams } from 'next/navigation';
-import useSWR from 'swr';
-
-const DetailMarketing = () => {
- const router = useRouter();
- const searchParams = useSearchParams();
-
- const soId = searchParams.get('marketingId');
-
- const {
- data: marketing,
- isLoading: isLoading,
- mutate: refreshMarketing,
- } = useSWR(soId, (id: number) => MarketingApi.getSingle(id));
-
- if (!soId) {
- router.back();
-
- return (
-
-
-
- );
- }
-
- if (!isLoading && (!marketing || isResponseError(marketing))) {
- router.replace('/404');
- return;
- }
-
- return (
-
- {isLoading && }
- {!isLoading && isResponseSuccess(marketing) && (
-
- )}
-
- );
-};
-
-export default DetailMarketing;
diff --git a/src/app/marketing/detail/sales-orders/edit/layout.tsx b/src/app/marketing/detail/sales-orders/edit/layout.tsx
deleted file mode 100644
index 7220dfa1..00000000
--- a/src/app/marketing/detail/sales-orders/edit/layout.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import SuspenseHelper from '@/components/helper/SuspenseHelper';
-
-const Layout = ({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) => {
- return {children};
-};
-
-export default Layout;
diff --git a/src/app/marketing/detail/sales-orders/edit/page.tsx b/src/app/marketing/detail/sales-orders/edit/page.tsx
deleted file mode 100644
index 19a098c5..00000000
--- a/src/app/marketing/detail/sales-orders/edit/page.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-'use client';
-
-import MarketingForm from '@/components/pages/marketing/form/MarketingForm';
-import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-import { MarketingApi } from '@/services/api/marketing/marketing';
-import { useRouter, useSearchParams } from 'next/navigation';
-import useSWR from 'swr';
-
-const EditSalesOrder = () => {
- const router = useRouter();
- const searchParams = useSearchParams();
-
- const soId = searchParams.get('marketingId');
-
- const {
- data: marketing,
- isLoading: isLoading,
- mutate: refreshMarketing,
- } = useSWR(`get-so-${soId}`, () =>
- MarketingApi.getSingle(soId ? parseInt(soId) : 0)
- );
-
- if (!soId) {
- router.back();
-
- return (
-
-
-
- );
- }
-
- if (!isLoading && (!marketing || isResponseError(marketing))) {
- router.replace('/404');
- return;
- }
- return (
-
- {isLoading && }
- {!isLoading && isResponseSuccess(marketing) && (
- {
- refreshMarketing();
- }}
- />
- )}
-
- );
-};
-export default EditSalesOrder;
diff --git a/src/components/pages/marketing/DeliveryOrderFormModal.tsx b/src/components/pages/marketing/DeliveryOrderFormModal.tsx
index 2cf4ef5c..7c953fe8 100644
--- a/src/components/pages/marketing/DeliveryOrderFormModal.tsx
+++ b/src/components/pages/marketing/DeliveryOrderFormModal.tsx
@@ -1,16 +1,10 @@
'use client';
import AlertErrorList from '@/components/helper/form/FormErrors';
-import { useSelect, OptionType } from '@/components/input/SelectInput';
+import { OptionType } from '@/components/input/SelectInput';
import Modal, { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
-import {
- mergeSOwithDO,
- SalesProductToFieldValues,
- DeliveryProductToFieldValues,
-} from '@/components/pages/marketing/form/MarketingForm';
import { DeliveryOrderProductFormValues } from '@/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.schema';
-import { SalesOrderProductFormValues } from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema';
import { isResponseSuccess, isResponseError } from '@/lib/api-helper';
import { formatCurrency, formatDate, formatTitleCase } from '@/lib/helper';
import {
@@ -18,16 +12,13 @@ import {
MarketingApi,
SalesOrderApi,
} from '@/services/api/marketing/marketing';
-import { CustomerApi } from '@/services/api/master-data';
-import { UserApi } from '@/services/api/user';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
-import { BaseApproval, CreatedUser } from '@/types/api/api-general';
+import { BaseApproval } from '@/types/api/api-general';
import {
CreateDeliveryOrderPayload,
Marketing,
UpdateDeliveryOrderPayload,
} from '@/types/api/marketing/marketing';
-import { Customer } from '@/types/api/master-data/customer';
import { useFormik } from 'formik';
import { Icon } from '@iconify/react';
import { useRouter, useSearchParams } from 'next/navigation';
@@ -47,6 +38,9 @@ import {
DeliveryOrderSchema,
getFilledMarketingFormInitialValues,
SalesOrderFormValues,
+ mergeSOwithDO,
+ SalesProductToFieldValues,
+ DeliveryProductToFieldValues,
} from '@/components/pages/marketing/form/MarketingForm.schema';
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
import RequirePermission from '@/components/helper/RequirePermission';
@@ -116,13 +110,6 @@ const DeliveryOrderFormModal = ({
const formRef = useRef(null);
const textareaRef = useRef(null);
- const [grandTotal, setGrandTotal] = useState(
- isResponseSuccess(marketing) &&
- marketing?.data.sales_order
- ?.map((item) => item.total_price)
- .reduce((a, b) => a + b, 0)
- );
-
const [formErrorMessage, setFormErrorMessage] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [selectedDeliveryProduct, setSelectedDeliveryProduct] =
@@ -505,6 +492,14 @@ const DeliveryOrderFormModal = ({
formik.setFieldValue('delivery_order', deliveryOrderValues);
}, [deliveryOrderValues]);
+ const grandTotal = useMemo(() => {
+ return deliveryOrderValues.reduce(
+ (total, product) =>
+ total + parseFloat((product.total_price as string) || '0'),
+ 0
+ );
+ }, [deliveryOrderValues]);
+
return (
<>
)}
-
-
+
+
{step == 2 && 'Ubah '} Informasi{' '}
{step == 2 ? 'Delivery' : 'Produk'}
{step === 1 && (
-
+
+
+
)}
{step === 2 && (
void;
-}) => {
- const router = useRouter();
- const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
- 'APPROVED'
- );
- const [grandTotal, setGrandTotal] = useState(
- initialValues?.sales_order
- ?.map((item) => item.total_price)
- .reduce((a, b) => a + b, 0)
- );
- const [isLoading, setIsLoading] = useState(false);
-
- const deleteModal = useModal();
- const confirmationModal = useModal();
- const deliveryModal = useModal();
- const {
- approvals,
- isLoading: isLoadingApproval,
- refresh: refreshApproval,
- } = useApprovalSteps({
- latestApproval: initialValues?.latest_approval,
- approvalLines: MARKETING_APPROVAL_LINE,
- moduleName: 'MARKETINGS',
- moduleId: initialValues?.id as number as unknown as string,
- });
-
- const approveClickHandler = () => {
- setApprovalAction('APPROVED');
- confirmationModal.openModal();
- };
-
- const rejectClickHandler = () => {
- setApprovalAction('REJECTED');
- confirmationModal.openModal();
- };
-
- const deleteClickHandler = () => {
- deleteModal.openModal();
- };
-
- const confirmationModalDeleteClickHandler = async () => {
- setIsLoading(true);
- const res = await MarketingApi.delete(initialValues?.id as number);
- deleteModal.closeModal();
- router.push('/marketing');
- toast.success(res?.message as string);
- setIsLoading(false);
- };
-
- const confirmationModalApproveClickHandler = async (notes: string) => {
- setIsLoading(true);
- const res = await SalesOrderApi.singleApproval(
- initialValues?.id as number,
- approvalAction,
- notes
- );
- setIsLoading(false);
- confirmationModal.closeModal();
- toast.success(res?.message as string);
- refresh?.();
- refreshApproval?.();
- };
-
- const confirmationModalDeliveryClickHandler = async (notes: string) => {
- setIsLoading(true);
- const res = await SalesOrderApi.delivery(
- initialValues?.id as number,
- notes
- );
- setIsLoading(false);
- deliveryModal.closeModal();
- toast.success(res?.message as string);
- refresh?.();
- refreshApproval?.();
- router.push(
- `/marketing/detail/delivery-orders/edit?marketingId=${initialValues?.id}`
- );
- };
-
- const approval = initialValues?.latest_approval;
- const isRejected = approval?.action == 'REJECTED';
- const isApproved = approval?.action == 'APPROVED';
-
- return (
- <>
-
-
2 ? 'Delivery Order' : 'Sales Order'}`}
- backUrl='/marketing'
- />
- {!isLoadingApproval && approvals && (
-
- )}
-
- {initialValues?.latest_approval?.step_number == 1 && (
- <>
-
-
-
-
-
-
-
- >
- )}
- {initialValues?.latest_approval?.step_number != 1 && (
- <>
-
-
-
- >
- )}
-
-
-
-
-
-
-
- |
- No. Sales Order
- |
- : |
-
- {initialValues?.so_number}
- |
-
- {Number(initialValues?.latest_approval?.step_number) > 2 && (
-
- |
- No. Delivery Order
- |
- : |
-
- {initialValues?.delivery_order
- ?.map((item) => item.do_number)
- .join(', ')}
- |
-
- )}
-
- | Nama Pelanggan |
- : |
- {initialValues?.customer?.name} |
-
-
- | Status |
- : |
-
-
-
- {isRejected
- ? 'Ditolak'
- : formatTitleCase(approval?.step_name || '')}
-
- |
-
-
- | Tanggal Penjualan |
- : |
- {formatDate(initialValues?.so_date, 'DD MMM yyyy')} |
-
-
- | Total Penjualan |
- : |
- {formatCurrency(grandTotal as number)} |
-
-
- | Catatan |
- : |
- {initialValues?.notes ?? '-'} |
-
-
- | Dokumen Penjualan |
- : |
-
-
- |
-
- {Number(initialValues?.latest_approval?.step_number) > 2 && (
-
- | Dokumen Pengiriman |
- : |
-
- {initialValues?.delivery_order?.map((item, index) => (
-
- ))}
- |
-
- )}
-
-
-
-
- {initialValues?.sales_order && (
-
-
- data={initialValues?.sales_order}
- columns={[
- {
- header: 'Kandang',
- accessorFn(row) {
- return row.product_warehouse.warehouse.name;
- },
- },
- {
- header: 'Produk',
- accessorFn(row) {
- return row.product_warehouse.product.name;
- },
- },
- {
- header: 'Harga Satuan (Rp)',
- accessorFn(row) {
- return formatCurrency(row.unit_price);
- },
- },
- {
- header: 'Total Bobot (Kg)',
- accessorFn(row) {
- return formatNumber(row.total_weight);
- },
- },
- {
- header: 'Kuantitas',
- accessorFn(row) {
- return formatNumber(row.qty);
- },
- },
- {
- header: 'Avg. Bobot (Kg)',
- accessorFn(row) {
- return formatNumber(row.avg_weight);
- },
- },
- {
- header: 'Total Penjualan (Rp)',
- accessorFn(row) {
- return formatCurrency(row.total_price);
- },
- },
- ]}
- className={{
- containerClassName: cn({
- 'mb-20':
- initialValues?.sales_order &&
- initialValues?.sales_order?.length === 0,
- }),
- tableWrapperClassName: 'overflow-x-auto min-h-full!',
- tableClassName: 'font-inter w-full table-auto min-h-full!',
- headerRowClassName: 'border-b border-b-gray-200',
- headerColumnClassName:
- 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
- bodyRowClassName: 'border-b border-b-gray-200',
- bodyColumnClassName:
- 'px-6 py-3 last:flex last:flex-row last:justify-end',
- paginationClassName: 'hidden',
- }}
- />
-
- )}
- {initialValues?.delivery_order && (
-
- {initialValues?.delivery_order.map((delivery, index) => {
- return (
-
-
-
-
- Nomor DO : {delivery.do_number}
-
-
-
- data={delivery.deliveries}
- columns={[
- {
- header: 'Tanggal Pengiriman',
- accessorFn() {
- return formatDate(
- delivery.delivery_date,
- 'DD MMM yyyy'
- );
- },
- },
- {
- header: 'No. Polisi',
- accessorFn(row) {
- return formatVechicleNumber(row.vehicle_number);
- },
- },
- {
- header: 'Kandang',
- accessorFn(row) {
- return row.product_warehouse.warehouse.name;
- },
- },
- {
- header: 'Produk',
- accessorFn(row) {
- return row.product_warehouse.product.name;
- },
- },
- {
- header: 'Harga Satuan (Rp)',
- accessorFn(row) {
- return formatCurrency(row.unit_price);
- },
- },
- {
- header: 'Total Bobot (Kg)',
- accessorFn(row) {
- return formatNumber(row.total_weight);
- },
- },
- {
- header: 'Kuantitas',
- accessorFn(row) {
- return formatNumber(row.qty);
- },
- },
- {
- header: 'Avg. Bobot (Kg)',
- accessorFn(row) {
- return formatNumber(row.avg_weight);
- },
- },
- {
- header: 'Total Penjualan (Rp)',
- accessorFn(row) {
- return formatCurrency(row.total_price);
- },
- },
- ]}
- className={{
- containerClassName: cn({
- 'mb-20':
- initialValues?.sales_order &&
- initialValues?.sales_order?.length === 0,
- }),
- tableWrapperClassName: 'overflow-x-auto min-h-full!',
- tableClassName:
- 'font-inter w-full table-auto min-h-full!',
- headerRowClassName: 'border-b border-b-gray-200',
- headerColumnClassName:
- 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
- bodyRowClassName: 'border-b border-b-gray-200',
- bodyColumnClassName:
- 'px-6 py-3 last:flex last:flex-row last:justify-end',
- paginationClassName: 'hidden',
- }}
- />
-
-
-
-
-
- );
- })}
-
- )}
-
- {initialValues?.latest_approval?.step_number != 3 && (
- <>
-
-
-
- >
- )}
-
-
-
-
-
-
-
-
- >
- );
-};
-
-export default MarketingDetail;
diff --git a/src/components/pages/marketing/detail/MarketingDetail.tsx b/src/components/pages/marketing/detail/MarketingDetail.tsx
deleted file mode 100644
index f8f7d269..00000000
--- a/src/components/pages/marketing/detail/MarketingDetail.tsx
+++ /dev/null
@@ -1,572 +0,0 @@
-'use client';
-
-import Button from '@/components/Button';
-import Card from '@/components/Card';
-import { FormHeader } from '@/components/helper/form/FormHeader';
-import { useModal } from '@/components/Modal';
-import ConfirmationModal from '@/components/modal/ConfirmationModal';
-import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
-import ApprovalSteps, {
- useApprovalSteps,
-} from '@/components/pages/ApprovalSteps';
-import Table from '@/components/Table';
-import { MARKETING_APPROVAL_LINE } from '@/config/approval-line';
-import {
- cn,
- formatCurrency,
- formatDate,
- formatNumber,
- formatTitleCase,
- formatVechicleNumber,
-} from '@/lib/helper';
-import {
- MarketingApi,
- SalesOrderApi,
-} from '@/services/api/marketing/marketing';
-import {
- BaseDelivery,
- BaseSalesOrder,
- Marketing,
-} from '@/types/api/marketing/marketing';
-import { Icon } from '@iconify/react';
-import { useRouter } from 'next/navigation';
-import { useState } from 'react';
-import toast from 'react-hot-toast';
-import SalesOrderExport from '@/components/pages/marketing/pdf/SalesOrderExport';
-import DeliveryOrderExport from '@/components/pages/marketing/pdf/DeliveryOrderExport';
-import RequirePermission from '@/components/helper/RequirePermission';
-import Badge from '@/components/Badge';
-
-const MarketingDetail = ({
- initialValues,
- refresh,
-}: {
- initialValues?: Marketing;
- refresh?: () => void;
-}) => {
- const router = useRouter();
- const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
- 'APPROVED'
- );
- const [grandTotal, setGrandTotal] = useState(
- initialValues?.sales_order
- ?.map((item) => item.total_price)
- .reduce((a, b) => a + b, 0)
- );
- const [isLoading, setIsLoading] = useState(false);
-
- const deleteModal = useModal();
- const confirmationModal = useModal();
- const deliveryModal = useModal();
- const {
- approvals,
- isLoading: isLoadingApproval,
- refresh: refreshApproval,
- } = useApprovalSteps({
- latestApproval: initialValues?.latest_approval,
- approvalLines: MARKETING_APPROVAL_LINE,
- moduleName: 'MARKETINGS',
- moduleId: initialValues?.id as number as unknown as string,
- });
-
- const approveClickHandler = () => {
- setApprovalAction('APPROVED');
- confirmationModal.openModal();
- };
-
- const rejectClickHandler = () => {
- setApprovalAction('REJECTED');
- confirmationModal.openModal();
- };
-
- const deleteClickHandler = () => {
- deleteModal.openModal();
- };
-
- const confirmationModalDeleteClickHandler = async () => {
- setIsLoading(true);
- const res = await MarketingApi.delete(initialValues?.id as number);
- deleteModal.closeModal();
- router.push('/marketing');
- toast.success(res?.message as string);
- setIsLoading(false);
- };
-
- const confirmationModalApproveClickHandler = async (notes: string) => {
- setIsLoading(true);
- const res = await SalesOrderApi.singleApproval(
- initialValues?.id as number,
- approvalAction,
- notes
- );
- setIsLoading(false);
- confirmationModal.closeModal();
- toast.success(res?.message as string);
- refresh?.();
- refreshApproval?.();
- };
-
- const confirmationModalDeliveryClickHandler = async (notes: string) => {
- setIsLoading(true);
- const res = await SalesOrderApi.delivery(
- initialValues?.id as number,
- notes
- );
- setIsLoading(false);
- deliveryModal.closeModal();
- toast.success(res?.message as string);
- refresh?.();
- refreshApproval?.();
- router.push(
- `/marketing/detail/delivery-orders/edit?marketingId=${initialValues?.id}`
- );
- };
-
- const approval = initialValues?.latest_approval;
- const isRejected = approval?.action == 'REJECTED';
- const isApproved = approval?.action == 'APPROVED';
-
- return (
- <>
-
-
2 ? 'Delivery Order' : 'Sales Order'}`}
- backUrl='/marketing'
- />
- {!isLoadingApproval && approvals && (
-
- )}
-
- {initialValues?.latest_approval?.step_number == 1 && (
- <>
-
-
-
-
-
-
-
- >
- )}
- {initialValues?.latest_approval?.step_number != 1 && (
- <>
-
-
-
- >
- )}
-
-
-
-
-
-
-
- |
- No. Sales Order
- |
- : |
-
- {initialValues?.so_number}
- |
-
- {Number(initialValues?.latest_approval?.step_number) > 2 && (
-
- |
- No. Delivery Order
- |
- : |
-
- {initialValues?.delivery_order
- ?.map((item) => item.do_number)
- .join(', ')}
- |
-
- )}
-
- | Nama Pelanggan |
- : |
- {initialValues?.customer?.name} |
-
-
- | Status |
- : |
-
-
-
- {isRejected
- ? 'Ditolak'
- : formatTitleCase(approval?.step_name || '')}
-
- |
-
-
- | Tanggal Penjualan |
- : |
- {formatDate(initialValues?.so_date, 'DD MMM yyyy')} |
-
-
- | Total Penjualan |
- : |
- {formatCurrency(grandTotal as number)} |
-
-
- | Catatan |
- : |
- {initialValues?.notes ?? '-'} |
-
-
- | Dokumen Penjualan |
- : |
-
-
- |
-
- {Number(initialValues?.latest_approval?.step_number) > 2 && (
-
- | Dokumen Pengiriman |
- : |
-
- {initialValues?.delivery_order?.map((item, index) => (
-
- ))}
- |
-
- )}
-
-
-
-
- {initialValues?.sales_order && (
-
-
- data={initialValues?.sales_order}
- columns={[
- {
- header: 'Kandang',
- accessorFn(row) {
- return row.product_warehouse.warehouse.name;
- },
- },
- {
- header: 'Produk',
- accessorFn(row) {
- return row.product_warehouse.product.name;
- },
- },
- {
- header: 'Harga Satuan (Rp)',
- accessorFn(row) {
- return formatCurrency(row.unit_price);
- },
- },
- {
- header: 'Total Bobot (Kg)',
- accessorFn(row) {
- return formatNumber(row.total_weight);
- },
- },
- {
- header: 'Kuantitas',
- accessorFn(row) {
- return formatNumber(row.qty);
- },
- },
- {
- header: 'Avg. Bobot (Kg)',
- accessorFn(row) {
- return formatNumber(row.avg_weight);
- },
- },
- {
- header: 'Total Penjualan (Rp)',
- accessorFn(row) {
- return formatCurrency(row.total_price);
- },
- },
- ]}
- className={{
- containerClassName: cn({
- 'mb-20':
- initialValues?.sales_order &&
- initialValues?.sales_order?.length === 0,
- }),
- tableWrapperClassName: 'overflow-x-auto min-h-full!',
- tableClassName: 'font-inter w-full table-auto min-h-full!',
- headerRowClassName: 'border-b border-b-gray-200',
- headerColumnClassName:
- 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
- bodyRowClassName: 'border-b border-b-gray-200',
- bodyColumnClassName:
- 'px-6 py-3 last:flex last:flex-row last:justify-end',
- paginationClassName: 'hidden',
- }}
- />
-
- )}
- {initialValues?.delivery_order && (
-
- {initialValues?.delivery_order.map((delivery, index) => {
- return (
-
-
-
-
- Nomor DO : {delivery.do_number}
-
-
-
- data={delivery.deliveries}
- columns={[
- {
- header: 'Tanggal Pengiriman',
- accessorFn() {
- return formatDate(
- delivery.delivery_date,
- 'DD MMM yyyy'
- );
- },
- },
- {
- header: 'No. Polisi',
- accessorFn(row) {
- return formatVechicleNumber(row.vehicle_number);
- },
- },
- {
- header: 'Kandang',
- accessorFn(row) {
- return row.product_warehouse.warehouse.name;
- },
- },
- {
- header: 'Produk',
- accessorFn(row) {
- return row.product_warehouse.product.name;
- },
- },
- {
- header: 'Harga Satuan (Rp)',
- accessorFn(row) {
- return formatCurrency(row.unit_price);
- },
- },
- {
- header: 'Total Bobot (Kg)',
- accessorFn(row) {
- return formatNumber(row.total_weight);
- },
- },
- {
- header: 'Kuantitas',
- accessorFn(row) {
- return formatNumber(row.qty);
- },
- },
- {
- header: 'Avg. Bobot (Kg)',
- accessorFn(row) {
- return formatNumber(row.avg_weight);
- },
- },
- {
- header: 'Total Penjualan (Rp)',
- accessorFn(row) {
- return formatCurrency(row.total_price);
- },
- },
- ]}
- className={{
- containerClassName: cn({
- 'mb-20':
- initialValues?.sales_order &&
- initialValues?.sales_order?.length === 0,
- }),
- tableWrapperClassName: 'overflow-x-auto min-h-full!',
- tableClassName:
- 'font-inter w-full table-auto min-h-full!',
- headerRowClassName: 'border-b border-b-gray-200',
- headerColumnClassName:
- 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
- bodyRowClassName: 'border-b border-b-gray-200',
- bodyColumnClassName:
- 'px-6 py-3 last:flex last:flex-row last:justify-end',
- paginationClassName: 'hidden',
- }}
- />
-
-
-
-
-
- );
- })}
-
- )}
-
- {initialValues?.latest_approval?.step_number != 3 && (
- <>
-
-
-
- >
- )}
-
-
-
-
-
-
-
-
- >
- );
-};
-
-export default MarketingDetail;
diff --git a/src/components/pages/marketing/form/MarketingForm.schema.ts b/src/components/pages/marketing/form/MarketingForm.schema.ts
index 4d86a2e7..0215217f 100644
--- a/src/components/pages/marketing/form/MarketingForm.schema.ts
+++ b/src/components/pages/marketing/form/MarketingForm.schema.ts
@@ -203,6 +203,24 @@ export const mergeSOwithDO = (
avg_weight: autofill ? so.avg_weight : delivery?.avg_weight,
total_price: autofill ? so.total_price : delivery?.total_price,
marketing_product: so, // jika ada, override
+ uom: autofill ? so.uom : delivery?.uom,
+ weight_per_convertion: autofill
+ ? so.weight_per_convertion
+ : delivery?.weight_per_convertion,
+ price_per_convertion: autofill
+ ? so.price_per_convertion
+ : delivery?.price_per_convertion,
+ convertion_unit: autofill
+ ? so.convertion_unit
+ : delivery?.convertion_unit,
+ marketing_type: autofill ? so.marketing_type : delivery?.marketing_type,
+ total_peti: autofill ? so.total_peti : delivery?.total_peti,
+ price_per_qty: autofill ? so.price_per_qty : delivery?.price_per_qty,
+ sisa_berat: autofill ? so.sisa_berat : delivery?.sisa_berat,
+ price_sisa_berat: autofill
+ ? so.price_sisa_berat
+ : delivery?.price_sisa_berat,
+ week: autofill ? so.week : delivery?.week,
} as DeliveryOrderProductFormValues;
});
};
diff --git a/src/components/pages/marketing/form/MarketingForm.tsx b/src/components/pages/marketing/form/MarketingForm.tsx
deleted file mode 100644
index d2d26bc2..00000000
--- a/src/components/pages/marketing/form/MarketingForm.tsx
+++ /dev/null
@@ -1,872 +0,0 @@
-'use client';
-
-import Button from '@/components/Button';
-import Card from '@/components/Card';
-import { FormHeader } from '@/components/helper/form/FormHeader';
-import DateInput from '@/components/input/DateInput';
-import SelectInput, {
- OptionType,
- useSelect,
-} from '@/components/input/SelectInput';
-import Modal, { useModal } from '@/components/Modal';
-import { formatCurrency, formatDate, formatTitleCase } from '@/lib/helper';
-import {
- BaseDeliveryOrder,
- BaseSalesOrder,
- CreateDeliveryOrderPayload,
- CreateSalesOrderPayload,
- CreateSalesOrderProductPayload,
- Marketing,
- UpdateDeliveryOrderPayload,
- UpdateSalesOrderPayload,
-} from '@/types/api/marketing/marketing';
-import { Icon } from '@iconify/react';
-import { memo, useCallback, useEffect, useMemo, useState } from 'react';
-import { Customer } from '@/types/api/master-data/customer';
-import { CustomerApi } from '@/services/api/master-data';
-import { useFormik } from 'formik';
-import {
- DeliveryOrderFormValues,
- DeliveryOrderSchema,
- SalesOrderFormValues,
- SalesOrderSchema,
-} from '@/components/pages/marketing/form/MarketingForm.schema';
-import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-import {
- DeliveryOrderApi,
- MarketingApi,
- SalesOrderApi,
-} from '@/services/api/marketing/marketing';
-import ConfirmationModal from '@/components/modal/ConfirmationModal';
-import toast from 'react-hot-toast';
-import { useRouter } from 'next/navigation';
-import DebouncedTextArea from '@/components/input/DebouncedTextArea';
-import SalesOrderProductForm from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm';
-import DeliveryOrderProductTable from '@/components/pages/marketing/form/table-view/DeliveryOrderProductTable';
-import DeliveryOrderProductForm from '@/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct';
-import { SalesOrderProductFormValues } from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema';
-import { DeliveryOrderProductFormValues } from '@/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.schema';
-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 MemoizedSalesOrderProductForm = memo(SalesOrderProductForm);
-const MemoizedDeliveryOrderProductTable = memo(DeliveryOrderProductTable);
-const MemoizedDeliveryOrderProductForm = memo(DeliveryOrderProductForm);
-
-// ================== EXTERNAL HELPER FUNCTION ==================
-export interface ProductCalculationFields {
- qty: string | number | undefined;
- unit_price: string | number | undefined;
- total_price: string | number | undefined;
- avg_weight: string | number | undefined;
- total_weight: string | number | undefined;
-}
-
-export const SalesProductToFieldValues = (
- product: BaseSalesOrder
-): SalesOrderProductFormValues => {
- return {
- id: product.id,
- vehicle_number: product.vehicle_number,
- kandang_id: product.product_warehouse.warehouse.id,
- kandang: {
- value: product.product_warehouse.warehouse.id,
- label: product.product_warehouse.warehouse.name,
- },
- product_warehouse: {
- value: product.product_warehouse.id,
- label: product.product_warehouse.product.name,
- },
- product_warehouse_id: product.product_warehouse.id,
- unit_price: product.unit_price,
- total_weight: product.total_weight,
- qty: product.qty,
- avg_weight: product.avg_weight,
- total_price: product.total_price,
- };
-};
-export const DeliveryProductToFieldValues = (
- salesOrders: BaseSalesOrder[],
- delivery: BaseDeliveryOrder
-): DeliveryOrderProductFormValues[] => {
- const data = delivery.deliveries.map((item) => {
- const soId = salesOrders.find(
- (so) => so.product_warehouse.id === item.product_warehouse.id
- )?.id;
- return {
- id: soId,
- unit_price: item.unit_price,
- total_weight: item.total_weight,
- qty: item.qty,
- avg_weight: item.avg_weight,
- total_price: item.total_price,
- vehicle_number: item.vehicle_number,
- delivery_date: formatDate(delivery.delivery_date, 'yyyy-MM-DD'),
- do_number: delivery.do_number,
- marketing_product_id: soId,
- marketing_product: {
- id: soId,
- vehicle_number: item.vehicle_number,
- kandang_id: item.product_warehouse.warehouse.id,
- kandang: {
- value: item.product_warehouse.warehouse.id,
- label: item.product_warehouse.warehouse.name,
- },
- product_warehouse: {
- value: item.product_warehouse.id,
- label: item.product_warehouse.product.name,
- },
- product_warehouse_id: item.product_warehouse.id,
- unit_price: item.unit_price,
- total_weight: item.total_weight,
- qty: item.qty,
- avg_weight: item.avg_weight,
- total_price: item.total_price,
- },
- } as DeliveryOrderProductFormValues;
- });
- return data;
-};
-export const mergeSOwithDO = (
- salesOrders: SalesOrderProductFormValues[],
- deliveryOrders: DeliveryOrderProductFormValues[],
- autofill?: boolean
-): DeliveryOrderProductFormValues[] => {
- return salesOrders.map((so) => {
- const delivery = deliveryOrders.find(
- (d) => d?.marketing_product_id === so.id
- );
-
- return {
- ...so, // nilai dasar dari sales order
- marketing_product_id: so.id,
- delivery_date: delivery?.delivery_date || undefined,
- do_number: delivery?.do_number || undefined,
- vehicle_number: delivery?.vehicle_number || so.vehicle_number,
- unit_price: autofill ? so.unit_price : delivery?.unit_price,
- total_weight: autofill ? so.total_weight : delivery?.total_weight,
- qty: autofill ? so.qty : delivery?.qty,
- avg_weight: autofill ? so.avg_weight : delivery?.avg_weight,
- total_price: autofill ? so.total_price : delivery?.total_price,
- marketing_product: so, // jika ada, override
- } as DeliveryOrderProductFormValues;
- });
-};
-export const recalculate = (
- field: string,
- values: ProductCalculationFields
-) => {
- const { qty, unit_price, total_price, avg_weight, total_weight } = values;
- const result: Partial = {};
- if (field == 'unit_price' || field == 'total_price' || field == 'qty') {
- if (qty && unit_price && (field == 'unit_price' || field == 'qty')) {
- result.total_price = Number(qty) * Number(unit_price);
- } else if (qty && total_price && field == 'total_price') {
- result.unit_price = Number(total_price) / Number(qty);
- }
- }
- if (field == 'avg_weight' || field == 'total_weight' || field == 'qty') {
- if (qty && avg_weight && (field == 'avg_weight' || field == 'qty')) {
- result.total_weight = Number(qty) * Number(avg_weight);
- } else if (qty && total_weight && field == 'total_weight') {
- result.avg_weight = Number(total_weight) / Number(qty);
- }
- }
- return result;
-};
-export const getSubmitField = (values: ProductCalculationFields) => {
- const { qty, unit_price, total_price, avg_weight, total_weight } = values;
-
- // Harga logic
- if (qty && unit_price && !total_price) {
- return 'unit_price';
- }
- if (qty && total_price && !unit_price) {
- return 'total_price';
- }
-
- // Bobot logic
- if (qty && avg_weight && !total_weight) {
- return 'avg_weight';
- }
- if (qty && total_weight && !avg_weight) {
- return 'total_weight';
- }
-
- // Tidak ada yang perlu dihitung
- return '';
-};
-
-const MarketingForm = ({
- formType = 'add',
- initialValues,
- afterSubmit,
-}: {
- formType?: 'add' | 'edit' | 'add_deliver' | 'edit_deliver';
- initialValues?: Marketing;
- afterSubmit?: () => void;
-}) => {
- const router = useRouter();
- const deleteModal = useModal();
-
- const [isLoading, setIsLoading] = useState(false);
- const [selectedMarketingProduct, setSelectedMarketingProduct] =
- useState(null);
- const [selectedDeliveryProduct, setSelectedDeliveryProduct] =
- useState(null);
- const [deliveryFormState, setDeliveryFormState] = useState<'add' | 'edit'>(
- 'add'
- );
- const [deliveryOrderValues, setDeliveryOrderValues] = useState<
- DeliveryOrderProductFormValues[]
- >(
- mergeSOwithDO(
- initialValues?.sales_order?.map(SalesProductToFieldValues) ?? [],
- initialValues?.delivery_order?.flatMap((delivery) =>
- DeliveryProductToFieldValues(initialValues.sales_order, delivery)
- ) ?? []
- )
- );
-
- // ================== REPEATER ==================
- const addSOModal = useModal();
- const addDOModal = useModal();
- const [rowSOSelection, setRowSOSelection] = useState>(
- {}
- );
- const selectedRowSOIds = Object.keys(rowSOSelection).map((item) =>
- parseInt(item)
- );
-
- // ================== FETCH OPTIONS ==================
- 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<
- SalesOrderFormValues & DeliveryOrderFormValues
- >(() => {
- return {
- so_date: initialValues?.so_date || undefined,
- 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,
- label: initialValues.customer.name,
- }
- : null,
- sales_order:
- initialValues?.sales_order?.map((product) =>
- SalesProductToFieldValues(product)
- ) ?? [],
- delivery_order: mergeSOwithDO(
- initialValues?.sales_order?.map(SalesProductToFieldValues) ?? [],
- initialValues?.delivery_order?.flatMap((delivery) =>
- DeliveryProductToFieldValues(initialValues.sales_order, delivery)
- ) ?? []
- ),
- };
- }, [initialValues]);
- const formik = useFormik({
- enableReinitialize: true,
- initialValues: formikInitialValues,
- validationSchema:
- formType == 'add_deliver' || formType == 'edit_deliver'
- ? DeliveryOrderSchema
- : SalesOrderSchema,
- validateOnMount: true,
- onSubmit: async (values) => {
- const payload =
- formType != 'add_deliver' && formType != 'edit_deliver'
- ? ({
- customer_id: values.customer_id as number,
- sales_person_id: values.sales_person_id as number,
- date: formatDate(values.so_date as string, 'yyyy-MM-DD'),
- notes: values.notes as string,
- marketing_products: values.sales_order.map((product) => {
- return {
- vehicle_number: product.vehicle_number as string,
- kandang_id: product.kandang_id as number,
- product_warehouse_id: product.product_warehouse_id as number,
- unit_price: parseFloat(product.unit_price as string),
- total_weight: parseFloat(product.total_weight as string),
- qty: parseFloat(product.qty as string),
- avg_weight: parseFloat(product.avg_weight as string),
- total_price: parseFloat(product.total_price as string),
- } as CreateSalesOrderProductPayload;
- }),
- } as CreateSalesOrderPayload)
- : ({
- marketing_id: initialValues?.id as number,
- delivery_products: values.delivery_order
- .map((product) => {
- if (Boolean(product.delivery_date)) {
- return {
- marketing_product_id:
- product.marketing_product_id as number,
- unit_price: parseFloat(product.unit_price as string),
- total_weight: parseFloat(product.total_weight as string),
- qty: parseFloat(product.qty as string),
- avg_weight: parseFloat(product.avg_weight as string),
- total_price: parseFloat(product.total_price as string),
- delivery_date: formatDate(
- product.delivery_date as string,
- 'yyyy-MM-DD'
- ),
- vehicle_number: product.vehicle_number,
- };
- }
- })
- .filter((item) => Boolean(item)),
- } as UpdateDeliveryOrderPayload);
- switch (formType) {
- case 'add':
- await createMarketingHandler(payload as CreateSalesOrderPayload);
- break;
- case 'edit':
- await updateMarketingHandler(payload as UpdateSalesOrderPayload);
- break;
- case 'add_deliver':
- await createDeliveryHandler(payload as CreateDeliveryOrderPayload);
- break;
- case 'edit_deliver':
- await updateDeliveryHandler(payload as UpdateDeliveryOrderPayload);
- break;
- default:
- break;
- }
- afterSubmit?.();
- },
- });
-
- const memoSalesOrder = formik.values.sales_order;
-
- // ================== FORM REPEATER HANDLER ==================
- const createMarketingHandler = async (values: CreateSalesOrderPayload) => {
- setIsLoading(true);
- const createMarketingRes = await SalesOrderApi.create(values);
- if (isResponseSuccess(createMarketingRes)) {
- toast.success(createMarketingRes?.message as string);
- router.push('/marketing');
- }
- if (isResponseError(createMarketingRes)) {
- toast.error(createMarketingRes?.message as string);
- }
- setIsLoading(false);
- };
- const updateMarketingHandler = async (values: UpdateSalesOrderPayload) => {
- setIsLoading(true);
- const updateMarketingRes = await SalesOrderApi.update(
- initialValues?.id as number,
- values
- );
- if (isResponseSuccess(updateMarketingRes)) {
- toast.success(updateMarketingRes?.message as string);
- router.push(`/marketing/detail?marketingId=${initialValues?.id}`);
- }
- if (isResponseError(updateMarketingRes)) {
- toast.error(updateMarketingRes?.message as string);
- }
- setIsLoading(false);
- };
- const createDeliveryHandler = async (values: CreateDeliveryOrderPayload) => {
- setIsLoading(true);
- const createDeliveryRes = await DeliveryOrderApi.create(values);
- if (isResponseSuccess(createDeliveryRes)) {
- toast.success(createDeliveryRes?.message as string);
- setDeliveryOrderValues(
- createDeliveryRes.data?.delivery_order?.flatMap((delivery) =>
- DeliveryProductToFieldValues(
- createDeliveryRes.data?.sales_order,
- delivery
- )
- ) ?? []
- );
- router.push(`/marketing/detail?marketingId=${initialValues?.id}`);
- }
- if (isResponseError(createDeliveryRes)) {
- toast.error(createDeliveryRes?.message as string);
- }
- setIsLoading(false);
- };
- const updateDeliveryHandler = async (values: UpdateDeliveryOrderPayload) => {
- setIsLoading(true);
- const updateDeliveryRes = await DeliveryOrderApi.update(
- initialValues?.id as number,
- values
- );
- if (isResponseSuccess(updateDeliveryRes)) {
- toast.success(updateDeliveryRes?.message as string);
- setDeliveryOrderValues(
- mergeSOwithDO(
- formik.values.sales_order,
- updateDeliveryRes.data?.delivery_order?.flatMap((delivery) =>
- DeliveryProductToFieldValues(
- updateDeliveryRes.data?.sales_order,
- delivery
- )
- ) ?? []
- )
- );
- router.push(`/marketing/detail?marketingId=${initialValues?.id}`);
- }
- if (isResponseError(updateDeliveryRes)) {
- toast.error(updateDeliveryRes?.message as string);
- }
- setIsLoading(false);
- };
-
- // ================== MARKETING HANDLER ==================
- const deleteMarketingHandler = async () => {
- setIsLoading(true);
- const deleteMarketingRes = await MarketingApi.delete(
- initialValues?.id as number
- );
- if (isResponseSuccess(deleteMarketingRes)) {
- toast.success(deleteMarketingRes?.message as string);
- }
- if (isResponseError(deleteMarketingRes)) {
- toast.error(deleteMarketingRes?.message as string);
- }
- setIsLoading(false);
- deleteModal.closeModal();
- router.push('/marketing');
- };
- const handleChangeCustomer = useCallback(
- (val: OptionType | OptionType[] | null) => {
- formik.setFieldValue('customer_id', (val as OptionType)?.value);
- formik.setFieldValue('customer', val as OptionType);
- },
- []
- );
- 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]);
-
- // ================== SALES ORDER HANDLER ==================
- const handleDeleteSO = useCallback(
- (id: number) => {
- const currentProducts = formik.values.sales_order;
- formik.setFieldValue(
- 'sales_order',
- currentProducts.filter((p) => p.id != id)
- );
- },
- [memoSalesOrder]
- );
- const handleEditSO = useCallback(
- (id: number) => {
- const currentProducts = formik.values.sales_order;
- const selectedProduct = currentProducts.find((p) => p.id == id);
- setSelectedMarketingProduct(selectedProduct ?? null);
- addSOModal.openModal();
- },
- [memoSalesOrder]
- );
- const handleBulkDeleteSO = useCallback(() => {
- const currentProducts = formik.values.sales_order;
- formik.setFieldValue(
- 'sales_order',
- currentProducts.filter(
- (product) => !selectedRowSOIds.includes(product.id ?? -1)
- )
- );
- setRowSOSelection({});
- }, [selectedRowSOIds, memoSalesOrder]);
- const handleAddSOClick = useCallback(() => {
- setSelectedMarketingProduct(null);
- addSOModal.openModal();
- }, [addSOModal]);
- const handleAddSubmitSO = useCallback(
- async (values: SalesOrderProductFormValues, id?: number) => {
- const currentProducts = formik.values.sales_order;
-
- const newValues = {
- ...values,
- id: values.id ?? Date.now(),
- };
-
- let updatedProducts = [];
-
- if (id) {
- // Overwrite
- updatedProducts = currentProducts.map((item) =>
- item.id === id ? newValues : item
- );
- } else {
- // Add new item
- updatedProducts = [...currentProducts, newValues];
- }
-
- formik.setFieldValue('sales_order', updatedProducts);
-
- addSOModal.closeModal();
- },
- [addSOModal, memoSalesOrder]
- );
-
- // ================== DELIVERY ORDER HANDLER ==================
- const handleEditDO = useCallback(
- (id: number, values?: DeliveryOrderProductFormValues) => {
- setDeliveryFormState('edit');
- const currentProducts = formik.values.delivery_order.find(
- (product) => product.id == id
- );
- setSelectedDeliveryProduct(values ?? currentProducts ?? null);
- addDOModal.openModal();
- },
- [addDOModal]
- );
- const handleAddDOClick = useCallback(() => {
- setDeliveryFormState('add');
- setSelectedDeliveryProduct(null);
- addDOModal.openModal();
- }, [addDOModal]);
- const handleAddSubmitDO = useCallback(
- async (values: DeliveryOrderProductFormValues) => {
- const newValues = {
- ...values,
- id: values.id ?? Date.now(),
- };
-
- setDeliveryOrderValues((prev) => [...prev, newValues]);
- addDOModal.closeModal();
- setSelectedDeliveryProduct(null);
- },
- [addDOModal]
- );
- const handleUpdateDO = useCallback(
- async (id: number, values: DeliveryOrderProductFormValues) => {
- setDeliveryOrderValues((prev) =>
- prev.map((product) =>
- product.id === id ? { ...product, ...values } : product
- )
- );
- addDOModal.closeModal();
- setSelectedDeliveryProduct(null);
- },
- [addDOModal]
- );
- const handleDeleteDO = useCallback(
- async (id: number) => {
- setDeliveryOrderValues((prev) =>
- prev.map((product) =>
- product.id === id
- ? {
- ...product,
- ...{
- unit_price: '',
- total_weight: '',
- qty: '',
- avg_weight: '',
- total_price: '',
- delivery_date: '',
- },
- }
- : product
- )
- );
- addDOModal.closeModal();
- setSelectedDeliveryProduct(null);
- },
- [addDOModal]
- );
-
- useEffect(() => {
- formik.setFieldValue('delivery_order', deliveryOrderValues);
- }, [deliveryOrderValues, initialValues]);
-
- const grandTotal = useMemo(() => {
- return memoSalesOrder.reduce(
- (total, product) =>
- total + parseFloat((product.total_price as string) || '0'),
- 0
- );
- }, [memoSalesOrder]);
-
- // ===== Formik Error List =====
- const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
-
- return (
- <>
-
-
- {/* Actions button */}
- {formType == 'edit' && (
-
-
-
-
-
- )}
-
- {/* Modals */}
-
-
-
-
Tambah Produk
-
-
-
-
-
-
-
-
-
-
-
- {selectedDeliveryProduct ? 'Edit' : 'Tambah'} Pengiriman
-
-
-
-
-
-
-
-
-
- >
- );
-};
-
-export default MarketingForm;
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 1fc4c7c0..4c20f05b 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
@@ -13,6 +13,30 @@ type DeliveryOrderProductSchemaType = {
vehicle_number: string | undefined;
delivery_date: string | undefined | null;
do_number?: string | undefined | null; // Uncertain
+ uom?: string | null | undefined;
+ convertion_unit?: {
+ value: string;
+ label: string;
+ } | null;
+ weight_per_convertion?: number | null | undefined;
+ price_per_convertion?: number | null | undefined;
+ marketing_type?: {
+ value: string;
+ label: string;
+ } | null;
+ total_peti?: number | null | undefined;
+ sisa_berat?: number | null | undefined;
+ price_sisa_berat?: number | null | undefined;
+ /** Harga per butir telur untuk TELUR + QTY */
+ price_per_qty?: number | null | undefined;
+ /** Week untuk ayam pullet */
+ week?:
+ | {
+ value?: number;
+ label?: string;
+ }
+ | null
+ | undefined;
};
export const DeliveryOrderProductSchema: Yup.ObjectSchema =
@@ -40,6 +64,43 @@ export const DeliveryOrderProductSchema: Yup.ObjectSchema
+ marketingType?.value?.toLowerCase() === 'ayam_pullet',
+ then: (schema) =>
+ schema
+ .shape({
+ value: Yup.number().required(
+ 'Week wajib diisi untuk Ayam Pullet!'
+ ),
+ label: Yup.string().required(
+ 'Week wajib diisi untuk Ayam Pullet!'
+ ),
+ })
+ .required('Week wajib diisi untuk Ayam Pullet!'),
+ otherwise: (schema) => schema.optional().notRequired(),
+ }),
});
export type DeliveryOrderProductFormValues = Yup.InferType<
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 9e735a95..850d88d2 100644
--- a/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx
+++ b/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useState } from 'react';
+import { useEffect, useMemo, useState } from 'react';
import {
DeliveryOrderProductFormValues,
DeliveryOrderProductSchema,
@@ -8,10 +8,10 @@ import Alert from '@/components/Alert';
import Button from '@/components/Button';
import NumberInput from '@/components/input/NumberInput';
import PatternInput from '@/components/input/PatternInput';
-import { formatVechicleNumber } from '@/lib/helper';
+import { formatTitleCase, formatVechicleNumber } from '@/lib/helper';
import DateInput from '@/components/input/DateInput';
import { BaseSalesOrder } from '@/types/api/marketing/marketing';
-import { SalesProductToFieldValues } from '@/components/pages/marketing/form/MarketingForm';
+import { SalesProductToFieldValues } from '@/components/pages/marketing/form/MarketingForm.schema';
import * as Yup from 'yup';
import { isResponseSuccess } from '@/lib/api-helper';
import AlertErrorList from '@/components/helper/form/FormErrors';
@@ -21,9 +21,13 @@ import { ProductApi } from '@/services/api/master-data';
import StatusBadge from '@/components/helper/StatusBadge';
import SelectInputRadio from '@/components/input/SelectInputRadio';
import { OptionType } from '@/components/input/SelectInput';
-
-const roundWeight = (value: number) => Number(value.toFixed(2));
-const roundPrice = (value: number) => Math.round(value);
+import {
+ MARKETING_CONVERTION_UNIT_OPTIONS,
+ MARKETING_TYPE_OPTIONS,
+} from '@/config/constant';
+import Dropdown from '@/components/Dropdown';
+import { Icon } from '@iconify/react';
+import { handleMarketingCalculation } from '@/lib/marketing-calculation';
const DeliveryOrderProductForm = ({
formState,
@@ -49,6 +53,35 @@ const DeliveryOrderProductForm = ({
);
const [currentInput, setCurrentInput] = useState('');
+ // Check jika ada sisa berat = total_weight - (weight_per_convertion * total_peti)
+ const initialSisaBerat =
+ initialValues?.total_weight &&
+ initialValues?.weight_per_convertion &&
+ initialValues?.total_peti
+ ? Number(initialValues.total_weight) -
+ Number(initialValues.weight_per_convertion) *
+ Number(initialValues.total_peti)
+ : 0;
+
+ const initialPricePerConvertion =
+ initialValues?.total_price &&
+ initialValues?.total_peti &&
+ Number(initialValues.total_peti) !== 0
+ ? (Number(initialValues.total_price) -
+ initialSisaBerat * Number(initialValues.unit_price || 0)) /
+ Number(initialValues.total_peti)
+ : 0;
+
+ const initialPriceSisaBerat =
+ initialValues?.total_price && initialValues?.total_peti
+ ? Number(initialValues.total_price) -
+ initialPricePerConvertion * Number(initialValues.total_peti)
+ : 0;
+
+ const [hasSisaBerat, setHasSisaBerat] = useState(
+ initialSisaBerat > 0
+ );
+
// ============ Fetch Data ============
const { data: productData } = useSWR(
selectedProduct?.value
@@ -60,6 +93,27 @@ const DeliveryOrderProductForm = ({
: undefined
);
+ // Options Week dari minggu 1 - 22
+ const optionsWeek = useMemo(() => {
+ return Array.from({ length: 22 }, (_, i) => ({
+ value: i + 1,
+ label: `Week ${i + 1}`,
+ }));
+ }, []);
+
+ const options = exisitingValues
+ ?.map((item) => {
+ if (!Boolean(item.qty)) {
+ return {
+ value: item.id,
+ label: `${item.marketing_product?.product_warehouse?.label} - ${item.marketing_product?.kandang?.label}`,
+ } as OptionType;
+ } else {
+ return null;
+ }
+ })
+ ?.filter((item) => item != null) as OptionType[];
+
const salesOrder = salesOrders.find(
(item) => item.id === initialValues?.marketing_product_id
);
@@ -77,6 +131,19 @@ const DeliveryOrderProductForm = ({
avg_weight: initialValues?.avg_weight || undefined,
total_price: initialValues?.total_price || undefined,
marketing_product: initialValues?.marketing_product || undefined,
+ uom: initialValues?.uom || '',
+ weight_per_convertion:
+ initialValues?.weight_per_convertion != null
+ ? Number(initialValues.weight_per_convertion)
+ : null,
+ price_per_convertion: initialPricePerConvertion,
+ convertion_unit: initialValues?.convertion_unit || null,
+ marketing_type: initialValues?.marketing_type || null,
+ total_peti: initialValues?.total_peti ?? null,
+ price_per_qty: initialValues?.price_per_qty ?? null,
+ sisa_berat: initialSisaBerat,
+ price_sisa_berat: initialPriceSisaBerat,
+ week: initialValues?.week ?? null,
},
isInitialValid: false,
validationSchema: Yup.object().shape({
@@ -124,6 +191,16 @@ const DeliveryOrderProductForm = ({
avg_weight: '',
total_price: '',
marketing_product: undefined,
+ total_peti: null,
+ price_per_qty: null,
+ price_sisa_berat: null,
+ sisa_berat: null,
+ convertion_unit: null,
+ marketing_type: null,
+ weight_per_convertion: null,
+ price_per_convertion: null,
+ uom: '',
+ week: null,
},
});
// setSelectedProduct(null);
@@ -132,94 +209,34 @@ const DeliveryOrderProductForm = ({
const handleBlurField = (field: string) => {
setCurrentInput(field);
- 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) {
- const tw = roundWeight(qty * avgWeight);
- formik.setFieldValue('total_weight', tw);
-
- // Hitung total_price berdasarkan unit_price × total_weight
- if (unitPrice > 0) {
- formik.setFieldValue('total_price', roundPrice(unitPrice * tw));
- }
- }
- break;
- }
-
- case 'avg_weight': {
- if (avgWeight > 0) {
- const tw = roundWeight(qty * avgWeight);
- formik.setFieldValue('total_weight', tw);
-
- // Hitung total_price berdasarkan unit_price × total_weight
- if (unitPrice > 0) {
- formik.setFieldValue('total_price', roundPrice(unitPrice * tw));
- }
- }
- break;
- }
-
- case 'unit_price': {
- if (unitPrice > 0 && totalWeight > 0) {
- // Hitung total_price berdasarkan unit_price × total_weight
- formik.setFieldValue(
- 'total_price',
- roundPrice(unitPrice * totalWeight)
- );
- }
- break;
- }
-
- // ===== TOTAL EDITABLE =====
- case 'total_weight': {
- if (totalWeight > 0) {
- formik.setFieldValue('avg_weight', roundWeight(totalWeight / qty));
-
- // Hitung ulang total_price berdasarkan unit_price × total_weight
- if (unitPrice > 0) {
- formik.setFieldValue(
- 'total_price',
- roundPrice(unitPrice * totalWeight)
- );
- }
- }
- break;
- }
-
- case 'total_price': {
- if (totalPrice > 0 && totalWeight > 0) {
- // Hitung unit_price berdasarkan total_price / total_weight
- formik.setFieldValue(
- 'unit_price',
- roundPrice(totalPrice / totalWeight)
- );
- }
- break;
- }
- }
+ handleMarketingCalculation(field, {
+ values: formik.values,
+ setFieldValue: formik.setFieldValue,
+ hasSisaBerat,
+ });
};
- const options = exisitingValues
- ?.map((item) => {
- if (!Boolean(item.qty)) {
- return {
- value: item.id,
- label: `${item.marketing_product?.product_warehouse?.label} - ${item.marketing_product?.kandang?.label}`,
- } as OptionType;
- } else {
- return null;
- }
- })
- ?.filter((item) => item != null) as OptionType[];
+ // Handler khusus untuk toggle sisa berat - langsung pakai nilai baru
+ const handleSisaBeratToggle = (newHasSisaBerat: boolean) => {
+ setHasSisaBerat(newHasSisaBerat);
+
+ if (!newHasSisaBerat) {
+ // Ketika OFF - set nilai ke 0 dan recalculate tanpa sisa
+ formik.setFieldValue('sisa_berat', 0);
+ formik.setFieldValue('price_sisa_berat', 0);
+ }
+
+ // Langsung trigger recalculation dengan hasSisaBerat yang baru
+ handleMarketingCalculation('total_peti', {
+ values: {
+ ...formik.values,
+ sisa_berat: newHasSisaBerat ? formik.values.sisa_berat : 0,
+ price_sisa_berat: newHasSisaBerat ? formik.values.price_sisa_berat : 0,
+ },
+ setFieldValue: formik.setFieldValue,
+ hasSisaBerat: newHasSisaBerat,
+ });
+ };
const { setValues: setFormikValues } = formik;
@@ -229,9 +246,6 @@ const DeliveryOrderProductForm = ({
handleResetForm();
} else {
setFormikValues(initialValues);
- // const value = exisitingValues?.find(
- // (item) => item.id === initialValues?.id
- // );
if (initialValues?.marketing_product_id) {
setSelectedProduct({
value: initialValues?.id,
@@ -243,7 +257,23 @@ const DeliveryOrderProductForm = ({
}, [initialValues]);
// ===== Formik Error List =====
- const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
+ const { formErrorList, close, handleFormSubmit } = useFormikErrorList(
+ formik,
+ {
+ onBeforeSubmit(e) {
+ e.preventDefault();
+ handleBlurField(currentInput);
+ formik.setFieldValue(
+ 'uom',
+ isResponseSuccess(productData) ? productData?.data?.uom?.name : ''
+ );
+ },
+ }
+ );
+
+ useEffect(() => {
+ handleBlurField('week');
+ }, [formik.values.week]);
return (
<>
@@ -252,214 +282,514 @@ const DeliveryOrderProductForm = ({
onSubmit={handleFormSubmit}
onReset={handleResetForm}
>
- {formikErrorMessage && (
- setFormErrorMessage('')} className='my-3 w-full'>
-
{formikErrorMessage}
-
- )}
-
- item.id === selectedProduct?.value
- )?.marketing_product?.product_warehouse?.label,
- } as OptionType)
- : null
- }
- onChange={(value) => {
- const selected = value as OptionType;
- setSelectedProduct(selected);
+
+ {formikErrorMessage && (
+
setFormErrorMessage('')}
+ className='my-3 w-full'
+ >
+
{formikErrorMessage}
+
+ )}
+
+ {/* Tanggal Pengiriman */}
+
+
+ {/* No. Polisi */}
+
+
+ {/* Produk */}
+
item.id === selectedProduct?.value
+ )?.marketing_product?.product_warehouse?.label,
+ } as OptionType)
+ : null
+ }
+ onChange={(value) => {
+ const selected = value as OptionType;
+ setSelectedProduct(selected);
+
+ const so = salesOrders?.find(
+ (item) => item.id === selected?.value
+ );
+ if (!so) {
+ formik.setValues({
+ ...formik.values,
+ marketing_product_id: undefined,
+ marketing_product: null,
+ qty: '',
+ unit_price: '',
+ total_price: '',
+ avg_weight: '',
+ total_weight: '',
+ vehicle_number: '',
+ });
+ return;
+ }
- const so = salesOrders?.find((item) => item.id === selected?.value);
- if (!so) {
formik.setValues({
...formik.values,
- marketing_product_id: undefined,
- marketing_product: null,
- qty: '',
- unit_price: '',
- total_price: '',
- avg_weight: '',
- total_weight: '',
- vehicle_number: '',
+ marketing_product_id: selected.value as number,
+ marketing_product: SalesProductToFieldValues(so),
+ qty: so.qty,
+ unit_price: so.unit_price,
+ total_price: so.total_price,
+ avg_weight: so.avg_weight,
+ total_weight: so.total_weight,
+ vehicle_number: so.vehicle_number,
});
- return;
+ }}
+ startAdornment={
+ selectedProduct && (
+ item.id === selectedProduct?.value
+ )?.marketing_product?.kandang?.label ?? ''
+ }
+ color='success'
+ className={{
+ badge: 'whitespace-nowrap w-fit font-semibold',
+ }}
+ />
+ )
}
+ isClearable
+ isError={Boolean(formik.errors.marketing_product_id)}
+ errorMessage={formik.errors.marketing_product_id}
+ required
+ />
- formik.setValues({
- ...formik.values,
- marketing_product_id: selected.value as number,
- marketing_product: SalesProductToFieldValues(so),
- qty: so.qty,
- unit_price: so.unit_price,
- total_price: so.total_price,
- avg_weight: so.avg_weight,
- total_weight: so.total_weight,
- vehicle_number: so.vehicle_number,
- });
- }}
- startAdornment={
- selectedProduct && (
- item.id === selectedProduct?.value
- )?.marketing_product?.kandang?.label ?? ''
- }
- color='success'
- className={{
- badge: 'whitespace-nowrap w-fit font-semibold',
- }}
+ {/* Kategori */}
+ {
+ formik.setFieldValue('marketing_type', val);
+ }}
+ isClearable
+ placeholder='Pilih Kategori'
+ isDisabled
+ />
+
+ {/* Konversi Satuan Telur */}
+ {formik.values.marketing_type &&
+ formik.values.marketing_type.value.toLowerCase() === 'telur' &&
+ (!formik.values.convertion_unit ||
+ formik.values.convertion_unit.value.toLowerCase() !== 'peti') && (
+ formik.setFieldValue('convertion_unit', val)}
+ isClearable
+ placeholder='Pilih Konversi Satuan'
/>
- )
- }
- isClearable
- isError={Boolean(formik.errors.marketing_product_id)}
- errorMessage={formik.errors.marketing_product_id}
- required
- />
+ )}
+ {formik.values.convertion_unit &&
+ formik.values.convertion_unit.value.toLowerCase() === 'peti' && (
+
+
+
+
+
+
+ {formatTitleCase(
+ formik.values.convertion_unit.value
+ )}
+
+
+
+
+ }
+ className={{
+ wrapper: 'relative',
+ content:
+ 'rounded-xl mt-1 border border-base-content/5 shadow-sm overflow-hidden min-w-68.5 sm:min-w-103.25 w-full',
+ }}
+ >
+
+
+
+
{
+ formik.setFieldValue(
+ 'weight_per_convertion',
+ Number(e.target.value)
+ );
+ setCurrentInput(e.target.name);
+ }}
+ onBlur={() => handleBlurField('weight_per_convertion')}
+ />
+
+
+ )}
-
- {
- formik.handleChange(e);
- setCurrentInput(e.target.name);
- }}
- onBlur={() => handleBlurField('qty')}
- isError={Boolean(formik.errors.qty)}
- errorMessage={formik.errors.qty}
- placeholder='Masukan Kuantitas'
- endAdornment={
-
-
- {isResponseSuccess(productData)
- ? productData?.data?.uom.name
- : ''}
+ {/* Konversi Satuan Week Pullet */}
+ {formik.values.marketing_type?.value.toLowerCase() ===
+ 'ayam_pullet' && (
+ {
+ formik.setFieldValue('week', val);
+ }}
+ placeholder='Pilih Week'
+ />
+ )}
+
+ {/* Total Peti */}
+ {formik.values.convertion_unit?.value.toLowerCase() === 'peti' && (
+ {
+ formik.handleChange(e);
+ setCurrentInput(e.target.name);
+ }}
+ onBlur={() => handleBlurField('total_peti')}
+ isError={
+ formik.touched.total_peti && Boolean(formik.errors.total_peti)
+ }
+ errorMessage={formik.errors.total_peti}
+ placeholder='Masukan Total Peti'
+ endAdornment={
+
+ Kg
+
+ }
+ bottomLabel={`1 ${formik.values.convertion_unit?.value.toLowerCase()} = ${formik.values.weight_per_convertion ?? 0} Kg`}
+ />
+ )}
+
+ {/* Avg. Bobot */}
+ {formik.values.marketing_type?.value.toLowerCase() === 'trading' ||
+ (formik.values.convertion_unit?.value.toLowerCase() !== 'peti' &&
+ formik.values.convertion_unit?.value.toLowerCase() !== 'kg' && (
+ {
+ 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'
+ />
+ ))}
+
+ {/* Total Bobot */}
+ {formik.values.marketing_type?.value.toLowerCase() !== 'trading' && (
+ {
+ formik.handleChange(e);
+ setCurrentInput(e.target.name);
+ }}
+ onBlur={() => handleBlurField('total_weight')}
+ isError={
+ formik.touched.total_weight &&
+ Boolean(formik.errors.total_weight)
+ }
+ errorMessage={formik.errors.total_weight}
+ placeholder='Masukan Total Bobot'
+ />
+ )}
+
+ {/* Kuantitas */}
+ {
+ formik.handleChange(e);
+ setCurrentInput(e.target.name);
+ }}
+ onBlur={() => handleBlurField('qty')}
+ isError={Boolean(formik.errors.qty)}
+ errorMessage={formik.errors.qty}
+ placeholder='Masukan Kuantitas'
+ endAdornment={
+
+
+ {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 +
+ ' ' +
+ (isResponseSuccess(productData)
+ ? productData?.data?.uom.name
+ : '')
+ : ''
+ }
+ />
+
+ {/* Harga per convertion unit (PETI / KG) */}
+ {(formik.values.convertion_unit?.value.toLowerCase() === 'peti' ||
+ formik.values.convertion_unit?.value.toLowerCase() === 'kg') && (
+ {
+ formik.handleChange(e);
+ setCurrentInput(e.target.name);
+ }}
+ onBlur={() => handleBlurField('price_per_convertion')}
+ isError={
+ formik.touched.price_per_convertion &&
+ Boolean(formik.errors.price_per_convertion)
+ }
+ errorMessage={formik.errors.price_per_convertion}
+ placeholder='Masukan Harga Satuan'
+ />
+ )}
+
+ {/* Harga per butir untuk TELUR + QTY */}
+ {formik.values.marketing_type?.value.toLowerCase() === 'telur' &&
+ formik.values.convertion_unit?.value.toLowerCase() === 'qty' && (
+ {
+ formik.setFieldValue('price_per_qty', Number(e.target.value));
+ setCurrentInput('price_per_qty');
+ }}
+ onBlur={() => handleBlurField('price_per_qty')}
+ isError={
+ formik.touched.price_per_qty &&
+ Boolean(formik.errors.price_per_qty)
+ }
+ errorMessage={formik.errors.price_per_qty}
+ placeholder='Masukan Harga per Butir'
+ />
+ )}
+
+ {/* Harga Satuan */}
+ {formik.values.convertion_unit?.value.toLowerCase() !== 'peti' &&
+ formik.values.convertion_unit?.value.toLowerCase() !== 'kg' && (
+ {
+ formik.handleChange(e);
+ setCurrentInput(e.target.name);
+ }}
+ onBlur={() => handleBlurField('unit_price')}
+ isError={Boolean(formik.errors.unit_price)}
+ errorMessage={formik.errors.unit_price}
+ placeholder='Masukan Harga Satuan'
+ />
+ )}
+
+ {/* Sisa kg diluar peti */}
+ {formik.values.convertion_unit?.value.toLowerCase() === 'peti' && (
+
+
+ handleSisaBeratToggle(!hasSisaBerat)}
+ className='toggle toggle-primary rounded-full before:rounded-full before:bg-base-content/50 border-base-content/50 checked:border-primary checked:bg-primary checked:before:bg-base-100'
+ />
+
+
+
+ Jika ada, masukan berat di luar peti
- }
- bottomLabel={
- formik.values.marketing_product_id
- ? 'Stok dijual: ' +
- salesOrders?.find(
- (item) => item.id === formik.values.marketing_product_id
- )?.qty +
- ' ' +
- (isResponseSuccess(productData)
- ? productData?.data?.uom.name
- : '')
- : ''
- }
- />
- {
- formik.handleChange(e);
- setCurrentInput(e.target.name);
- }}
- onBlur={() => handleBlurField('unit_price')}
- isError={Boolean(formik.errors.unit_price)}
- errorMessage={formik.errors.unit_price}
- placeholder='Masukan Harga Satuan'
- />
- {
- 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('total_weight')}
- isError={Boolean(formik.errors.total_weight)}
- errorMessage={formik.errors.total_weight}
- placeholder='Masukan Total Bobot'
- />
+ )}
- {
- formik.handleChange(e);
- setCurrentInput(e.target.name);
- }}
- onBlur={() => handleBlurField('total_price')}
- isError={Boolean(formik.errors.total_price)}
- errorMessage={formik.errors.total_price}
- placeholder='Masukan Total Penjualan'
- />
+ {hasSisaBerat && (
+ <>
+ {
+ formik.handleChange(e);
+ setCurrentInput(e.target.name);
+ }}
+ onBlur={() => handleBlurField('sisa_berat')}
+ isError={
+ formik.touched.sisa_berat && Boolean(formik.errors.sisa_berat)
+ }
+ errorMessage={formik.errors.sisa_berat}
+ placeholder='Masukan Sisa Berat'
+ />
+ {
+ formik.handleChange(e);
+ setCurrentInput(e.target.name);
+ }}
+ onBlur={() => handleBlurField('price_sisa_berat')}
+ isError={
+ formik.touched.price_sisa_berat &&
+ Boolean(formik.errors.price_sisa_berat)
+ }
+ errorMessage={formik.errors.price_sisa_berat}
+ placeholder='Masukan Harga Sisa Berat'
+ />
+ >
+ )}
-
+ {/* Total Penjualan */}
+ {
+ formik.handleChange(e);
+ setCurrentInput(e.target.name);
+ }}
+ onBlur={() => handleBlurField('total_price')}
+ isError={
+ formik.touched.total_price && Boolean(formik.errors.total_price)
+ }
+ errorMessage={formik.errors.total_price}
+ placeholder='Masukan Total Penjualan'
+ />
+
+
-