diff --git a/src/app/marketing/sales-orders/detail/edit/page.tsx b/src/app/marketing/sales-orders/detail/edit/page.tsx
index 86cafcb6..660468f3 100644
--- a/src/app/marketing/sales-orders/detail/edit/page.tsx
+++ b/src/app/marketing/sales-orders/detail/edit/page.tsx
@@ -12,8 +12,9 @@ const EditSalesOrder = () => {
const soId = searchParams.get('salesOrderId');
- const { data: marketing, isLoading: isLoading } = useSWR(soId, (id: number) =>
- MarketingApi.getSingle(id)
+ const { data: marketing, isLoading: isLoading } = useSWR(
+ `get-so-${soId}`,
+ () => MarketingApi.getSingle(soId ? parseInt(soId) : 0)
);
if (!soId) {
diff --git a/src/app/marketing/sales-orders/detail/page.tsx b/src/app/marketing/sales-orders/detail/page.tsx
index 22d2651c..0ac71f56 100644
--- a/src/app/marketing/sales-orders/detail/page.tsx
+++ b/src/app/marketing/sales-orders/detail/page.tsx
@@ -12,9 +12,11 @@ const DetailSalesOrder = () => {
const soId = searchParams.get('salesOrderId');
- const { data: marketing, isLoading: isLoading } = useSWR(soId, (id: number) =>
- MarketingApi.getSingle(id)
- );
+ const {
+ data: marketing,
+ isLoading: isLoading,
+ mutate: refreshMarketing,
+ } = useSWR(soId, (id: number) => MarketingApi.getSingle(id));
if (!soId) {
router.back();
@@ -35,7 +37,10 @@ const DetailSalesOrder = () => {
{isLoading && }
{!isLoading && isResponseSuccess(marketing) && (
-
+
)}
);
diff --git a/src/components/pages/marketing/sales-orders/SalesOrderTable.tsx b/src/components/pages/marketing/sales-orders/SalesOrderTable.tsx
index 1e8edaba..cb7a9649 100644
--- a/src/components/pages/marketing/sales-orders/SalesOrderTable.tsx
+++ b/src/components/pages/marketing/sales-orders/SalesOrderTable.tsx
@@ -12,7 +12,12 @@ import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector';
import { TableToolbar } from '@/components/table/TableToolbar';
import { ROWS_OPTIONS } from '@/config/constant';
import { isResponseSuccess } from '@/lib/api-helper';
-import { cn, formatCurrency, formatVechicleNumber } from '@/lib/helper';
+import {
+ cn,
+ formatCurrency,
+ formatDate,
+ formatVechicleNumber,
+} from '@/lib/helper';
import { MarketingApi } from '@/services/api/marketing/marketing';
import { useTableFilter } from '@/services/hooks/useTableFilter';
import { Marketing, MarketingProduct } from '@/types/api/marketing/marketing';
@@ -216,12 +221,15 @@ const SalesOrderTable = () => {
),
},
{
- accessorKey: 'so_number',
+ accessorKey: 'name',
header: 'No. Order',
},
{
accessorKey: 'so_date',
header: 'Tanggal',
+ cell: (props) => {
+ return formatDate(props.row.original.so_date, 'DD MMM yyyy');
+ },
},
{
accessorKey: 'approval.step_name',
@@ -232,8 +240,18 @@ const SalesOrderTable = () => {
header: 'Customer',
},
{
- accessorKey: 'grand_total',
+ accessorFn: (row) =>
+ row.marketing_products
+ ?.map((product) => product.total_price)
+ .reduce((a, b) => a + b, 0) ?? 0,
header: 'Grand Total',
+ cell: (props) => {
+ return formatCurrency(
+ props.row.original?.marketing_products
+ ?.map((product) => product.total_price)
+ .reduce((a, b) => a + b, 0) ?? 0
+ );
+ },
},
{
accessorKey: 'marketing_products.length',
diff --git a/src/components/pages/marketing/sales-orders/detail/SalesOrderDetail.tsx b/src/components/pages/marketing/sales-orders/detail/SalesOrderDetail.tsx
index e67a0644..e0262d9d 100644
--- a/src/components/pages/marketing/sales-orders/detail/SalesOrderDetail.tsx
+++ b/src/components/pages/marketing/sales-orders/detail/SalesOrderDetail.tsx
@@ -5,10 +5,15 @@ import Card from '@/components/Card';
import { FormHeader } from '@/components/helper/form/FormHeader';
import { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
+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,
formatVechicleNumber,
} from '@/lib/helper';
@@ -20,27 +25,42 @@ import toast from 'react-hot-toast';
const SalesOrderDetail = ({
initialValues,
- refreshValues,
+ refresh,
}: {
initialValues?: Marketing;
- refreshValues?: () => void;
+ refresh?: () => void;
}) => {
- const [approvalAction, setApprovalAction] = useState<'approve' | 'reject'>(
- 'approve'
+ const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
+ 'APPROVED'
+ );
+ const [grandTotal, setGrandTotal] = useState(
+ initialValues?.marketing_products
+ ?.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?.approval,
+ approvalLines: MARKETING_APPROVAL_LINE,
+ moduleName: 'MARKETINGS',
+ moduleId: initialValues?.id as number as unknown as string,
+ });
const approveClickHandler = () => {
- setApprovalAction('approve');
+ setApprovalAction('APPROVED');
confirmationModal.openModal();
};
const rejectClickHandler = () => {
- setApprovalAction('reject');
+ setApprovalAction('REJECTED');
confirmationModal.openModal();
};
@@ -54,23 +74,24 @@ const SalesOrderDetail = ({
const confirmationModalDeleteClickHandler = async () => {
setIsLoading(true);
- // await MarketingApi.delete(initialValues?.id as number);
+ const res = await MarketingApi.delete(initialValues?.id as number);
setIsLoading(false);
deleteModal.closeModal();
- toast.success('Successfully deleted Sales Order!');
- refreshValues?.();
+ toast.success(res?.message as string);
+ refresh?.();
};
const confirmationModalApproveClickHandler = async () => {
setIsLoading(true);
- // await MarketingApi.singleApproval(
- // initialValues?.id as number,
- // approvalAction
- // );
+ const res = await MarketingApi.singleApproval(
+ initialValues?.id as number,
+ approvalAction
+ );
setIsLoading(false);
confirmationModal.closeModal();
- toast.success('Successfully approved Sales Order!');
- refreshValues?.();
+ toast.success(res?.message as string);
+ refresh?.();
+ refreshApproval?.();
};
const confirmationModalDeliveryClickHandler = async () => {
@@ -79,7 +100,7 @@ const SalesOrderDetail = ({
setIsLoading(false);
deliveryModal.closeModal();
toast.success('Successfully delivered Sales Order!');
- refreshValues?.();
+ refresh?.();
};
return (
@@ -89,6 +110,9 @@ const SalesOrderDetail = ({
title='Detail Sales Order'
backUrl='/marketing/sales-orders'
/>
+ {!isLoadingApproval && approvals && (
+
+ )}
{initialValues?.approval?.step_number != 3 && (
<>
@@ -117,6 +141,7 @@ const SalesOrderDetail = ({
)}
+
: |
- {initialValues?.so_number} |
+ {initialValues?.name} |
| Nama Pelanggan |
@@ -146,14 +171,12 @@ const SalesOrderDetail = ({
| Tanggal Penjualan |
: |
- {initialValues?.so_date} |
+ {formatDate(initialValues?.so_date, 'DD MMM yyyy')} |
| Total Penjualan |
: |
-
- {formatCurrency(initialValues?.grand_total as number)}
- |
+ {formatCurrency(grandTotal as number)} |
| Catatan |
@@ -178,7 +201,7 @@ const SalesOrderDetail = ({
header: 'No. Polisi',
accessorFn(row) {
return formatVechicleNumber(
- row.marketing_delivery_products?.vehicle_number as string
+ row.delivery_product?.vehicle_number as string
);
},
},
@@ -275,14 +298,14 @@ const SalesOrderDetail = ({
/>
= Yup.object({
- customer_id: Yup.number().required('Customer wajib diisi!'),
- customer: Yup.object({
- value: Yup.number().required(),
- label: Yup.string().required(),
- }).nullable(),
- so_date: Yup.string().required('Tanggal wajib diisi!'),
- notes: Yup.string().required('Catatan wajib diisi!'),
- marketing_products: Yup.array()
- .of(MarketingProductSchema)
- .min(1, 'Minimal harus ada 1 produk!')
- .required('Produk wajib diisi!'),
-});
+type SalesOrderSchemaType = MarketingSchemaType & {
+ marketing_products: SalesOrderProductFormValues[];
+};
-export const UpdateMarketingSchema = MarketingSchema;
+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!'),
+ customer: Yup.object({
+ value: Yup.number().required(),
+ label: Yup.string().required(),
+ }).nullable(),
+ so_date: Yup.string().required('Tanggal wajib diisi!'),
+ notes: Yup.string().required('Catatan wajib diisi!'),
+ marketing_products: Yup.array()
+ .of(SalesOrderProductSchema)
+ .min(1, 'Produk wajib diisi!')
+ .required('Produk wajib diisi!'),
+ });
-export type MarketingFormValues = Yup.InferType;
+export const UpdateSalesOrderSchema = SalesOrderSchema;
+
+export type SalesOrderFormValues = Yup.InferType;
diff --git a/src/components/pages/marketing/sales-orders/form/SalesForm.tsx b/src/components/pages/marketing/sales-orders/form/SalesForm.tsx
index 2973f6ad..9286fe93 100644
--- a/src/components/pages/marketing/sales-orders/form/SalesForm.tsx
+++ b/src/components/pages/marketing/sales-orders/form/SalesForm.tsx
@@ -12,10 +12,10 @@ import TextArea from '@/components/input/TextArea';
import Modal, { useModal } from '@/components/Modal';
import * as TanStack from '@tanstack/react-table';
import Table from '@/components/Table'; // Keep this import
-import { cn, formatCurrency, formatNumber } from '@/lib/helper';
+import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
import {
- CreateMarketingPayload,
- CreateMarketingProductPayload,
+ CreateSalesOrderPayload,
+ CreateSalesOrderProductPayload,
Marketing,
MarketingProduct,
} from '@/types/api/marketing/marketing';
@@ -26,10 +26,10 @@ import CheckboxInput from '@/components/input/CheckboxInput';
import { Customer } from '@/types/api/master-data/customer';
import { CustomerApi } from '@/services/api/master-data';
import { useFormik } from 'formik';
-import { MarketingFormValues, MarketingSchema } from './SalesForm.schema';
+import { SalesOrderFormValues, SalesOrderSchema } from './SalesForm.schema';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { MarketingApi } from '@/services/api/marketing/marketing';
-import { MarketingProductFormValues } from './repeater/MarketingProduct.schema';
+import { SalesOrderProductFormValues } from './repeater/MarketingProduct.schema';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import toast from 'react-hot-toast';
import { useRouter } from 'next/navigation';
@@ -37,9 +37,11 @@ import { useRouter } from 'next/navigation';
const SalesForm = ({
formType = 'add',
initialValues,
+ afterSubmit,
}: {
formType?: 'add' | 'edit';
initialValues?: Marketing;
+ afterSubmit?: () => void;
}) => {
const router = useRouter();
const addProductModal = useModal();
@@ -61,11 +63,9 @@ const SalesForm = ({
parseInt(item)
);
const [grandTotal, setGrandTotal] = useState(
- initialValues?.grand_total ?? 0
- );
- const marketingProducts = useMemo(
- () => rawMarketingProducts,
- [rawMarketingProducts]
+ initialValues?.marketing_products
+ ?.map((item) => item.total_price)
+ .reduce((a, b) => a + b, 0) ?? 0
);
const {
@@ -91,8 +91,8 @@ const SalesForm = ({
const handleAddSubmitProduct = useCallback(
async (
- tableValue: CreateMarketingProductPayload,
- fieldValues: MarketingProductFormValues
+ tableValue: CreateSalesOrderProductPayload,
+ fieldValues: SalesOrderProductFormValues
) => {
const newMarketingProduct: MarketingProduct = {
id: rawMarketingProducts.length + 1,
@@ -102,10 +102,10 @@ const SalesForm = ({
qty: tableValue.qty as number,
avg_weight: tableValue.avg_weight as number,
total_price: tableValue.total_price as number,
- marketing_delivery_products: {
+ delivery_product: {
id: rawMarketingProducts.length + 1,
vehicle_number: tableValue.vehicle_number as string,
- delivery_date: tableValue.delivery_date as string,
+ delivery_date: '' as string,
unit_price: tableValue.unit_price as number,
total_weight: tableValue.total_weight as number,
qty: tableValue.qty as number,
@@ -133,32 +133,30 @@ const SalesForm = ({
[selectedCustomer, setSelectedCustomer]
);
- const createMarketingHandler = async (values: CreateMarketingPayload) => {
+ const createMarketingHandler = async (values: CreateSalesOrderPayload) => {
console.log(values);
const createMarketingRes = await MarketingApi.create(values);
if (isResponseSuccess(createMarketingRes)) {
- console.log(createMarketingRes);
+ toast.success(createMarketingRes?.message as string);
+ router.push('/marketing/sales-orders');
}
if (isResponseError(createMarketingRes)) {
- console.log(createMarketingRes);
+ toast.error(createMarketingRes?.message as string);
}
- toast.success('Successfully created Sales Order!');
- router.push('/marketing/sales-orders');
};
- const updateMarketingHandler = async (values: CreateMarketingPayload) => {
+ const updateMarketingHandler = async (values: CreateSalesOrderPayload) => {
console.log(values);
- const createMarketingRes = await MarketingApi.update(
+ const updateMarketingRes = await MarketingApi.update(
initialValues?.id as number,
values
);
- if (isResponseSuccess(createMarketingRes)) {
- console.log(createMarketingRes);
+ if (isResponseSuccess(updateMarketingRes)) {
+ toast.success(updateMarketingRes?.message as string);
+ router.push('/marketing/sales-orders');
}
- if (isResponseError(createMarketingRes)) {
- console.log(createMarketingRes);
+ if (isResponseError(updateMarketingRes)) {
+ toast.error(updateMarketingRes?.message as string);
}
- toast.success('Successfully updated Sales Order!');
- router.push('/marketing/sales-orders');
};
const deleteMarketingHandler = async () => {
setIsLoading(true);
@@ -180,9 +178,9 @@ const SalesForm = ({
const MarketingProductToFieldValues = (
product: MarketingProduct
- ): MarketingProductFormValues => {
+ ): SalesOrderProductFormValues => {
return {
- vehicle_number: product.marketing_delivery_products?.vehicle_number,
+ vehicle_number: product.delivery_product?.vehicle_number,
kandang_id: product.product_warehouse.warehouse.id,
kandang: {
value: product.product_warehouse.warehouse.id,
@@ -196,19 +194,17 @@ const SalesForm = ({
unit_price: product.unit_price,
total_weight: product.total_weight,
qty: product.qty,
- uom: product.product_warehouse?.product?.uom?.name,
avg_weight: product.avg_weight,
total_price: product.total_price,
- delivery_date: product.marketing_delivery_products?.delivery_date,
};
};
- const formik = useFormik({
- enableReinitialize: true,
- initialValues: {
+ const formikInitialValues = useMemo(() => {
+ 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,
customer: {
value: initialValues?.customer?.id as number,
label: initialValues?.customer?.name as string,
@@ -217,15 +213,33 @@ const SalesForm = ({
initialValues?.marketing_products?.map((product) =>
MarketingProductToFieldValues(product)
) ?? [],
- },
- validationSchema: MarketingSchema,
+ };
+ }, [initialValues]);
+
+ const formik = useFormik({
+ enableReinitialize: true,
+ initialValues: formikInitialValues,
+ validationSchema: SalesOrderSchema,
+ validateOnMount: true,
onSubmit: async (values) => {
const payload = {
customer_id: values.customer_id as number,
- date: values.so_date as string,
+ 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.marketing_products,
- } as CreateMarketingPayload;
+ marketing_products: values.marketing_products.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;
switch (formType) {
case 'add':
createMarketingHandler(payload);
@@ -236,6 +250,7 @@ const SalesForm = ({
default:
break;
}
+ afterSubmit?.();
},
});
@@ -245,6 +260,33 @@ const SalesForm = ({
formikSetValues(formik.initialValues);
}, [formikSetValues, formik.initialValues]);
+ useEffect(() => {
+ // Konversi array MarketingProduct ke format SalesOrderProductFormValues
+ const newMarketingProductValues = rawMarketingProducts.map((product) =>
+ MarketingProductToFieldValues(product)
+ );
+
+ // Hitung Grand Total baru
+ const newGrandTotal = rawMarketingProducts.reduce(
+ (total, product) => total + product.total_price,
+ 0
+ );
+
+ // Perbarui nilai formik.values.marketing_products
+ formik.setFieldValue(
+ 'marketing_products',
+ newMarketingProductValues,
+ false
+ );
+ // Parameter ketiga (false) untuk menghindari validasi secara langsung
+
+ // Perbarui state grandTotal
+ setGrandTotal(newGrandTotal);
+
+ // Reset row selection setiap kali daftar produk berubah (opsional, tapi disarankan)
+ setRowSelection({});
+ }, [rawMarketingProducts]);
+
const columns = useMemo(
() => [
{
@@ -273,7 +315,7 @@ const SalesForm = ({
},
{
accessorFn: (row: MarketingProduct) =>
- row.marketing_delivery_products?.vehicle_number,
+ row.delivery_product?.vehicle_number,
header: 'No. Polisi',
},
{
@@ -321,7 +363,7 @@ const SalesForm = ({
),
},
],
- [handleDeleteProduct] // dependensi tunggal
+ [handleDeleteProduct, initialValues, rawMarketingProducts] // dependensi tunggal
);
return (
@@ -372,10 +414,15 @@ const SalesForm = ({
wrapper: 'bg-white w-full',
}}
>
+ {JSON.stringify(formik.values.marketing_products)}
+
+ {JSON.stringify(formik.values.marketing_products)}
+
+ {JSON.stringify(formik.errors)}
rowSelection={rowSelection}
setRowSelection={setRowSelection}
- data={marketingProducts}
+ data={rawMarketingProducts}
columns={columns}
className={{
tableWrapperClassName: 'overflow-x-auto min-h-full!',
diff --git a/src/components/pages/marketing/sales-orders/form/SalesFormRepeater.ts b/src/components/pages/marketing/sales-orders/form/SalesFormRepeater.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/components/pages/marketing/sales-orders/form/SalesFromRepeater.tsx b/src/components/pages/marketing/sales-orders/form/SalesFromRepeater.tsx
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/components/pages/marketing/sales-orders/form/repeater/MarketingProduct.schema.ts b/src/components/pages/marketing/sales-orders/form/repeater/MarketingProduct.schema.ts
index 58705af3..084eb4f8 100644
--- a/src/components/pages/marketing/sales-orders/form/repeater/MarketingProduct.schema.ts
+++ b/src/components/pages/marketing/sales-orders/form/repeater/MarketingProduct.schema.ts
@@ -15,13 +15,13 @@ type MarketingProductSchemaType = {
unit_price: string | number | undefined;
total_weight: string | number | undefined;
qty: string | number | undefined;
- uom: string | undefined | null;
avg_weight: string | number | undefined;
total_price: string | number | undefined;
- delivery_date?: string | undefined | null;
};
-export const MarketingProductSchema: Yup.ObjectSchema =
+type SalesOrderProductSchemaType = MarketingProductSchemaType;
+
+const MarketingProductSchema: Yup.ObjectSchema =
Yup.object({
vehicle_number: Yup.string().required('No. Polisi wajib diisi!'),
kandang: Yup.object({
@@ -47,16 +47,17 @@ export const MarketingProductSchema: Yup.ObjectSchema =
+ MarketingProductSchema;
+
+export type SalesOrderProductFormValues = Yup.InferType<
+ typeof SalesOrderProductSchema
>;
diff --git a/src/components/pages/marketing/sales-orders/form/repeater/MarketingProductForm.tsx b/src/components/pages/marketing/sales-orders/form/repeater/MarketingProductForm.tsx
index 0069a61e..2ce990dd 100644
--- a/src/components/pages/marketing/sales-orders/form/repeater/MarketingProductForm.tsx
+++ b/src/components/pages/marketing/sales-orders/form/repeater/MarketingProductForm.tsx
@@ -1,17 +1,15 @@
'use client';
-import TextInput from '@/components/input/TextInput';
import {
- CreateMarketingPayload,
- CreateMarketingProductPayload,
+ CreateSalesOrderProductPayload,
MarketingProduct,
} from '@/types/api/marketing/marketing';
import { useFormik } from 'formik';
import {
- MarketingProductFormValues,
- MarketingProductSchema,
+ SalesOrderProductFormValues,
+ SalesOrderProductSchema,
} from './MarketingProduct.schema';
-import { RefObject, use, useEffect, useRef, useState } from 'react';
+import { RefObject, useEffect, useState } from 'react';
import SelectInput, {
OptionType,
useSelect,
@@ -36,8 +34,8 @@ const MarketingProductForm = ({
data: MarketingProduct[];
modalRef?: RefObject;
onSubmitForm?: (
- tableValues: CreateMarketingProductPayload,
- fieldValues: MarketingProductFormValues
+ tableValues: CreateSalesOrderProductPayload,
+ fieldValues: SalesOrderProductFormValues
) => Promise;
}) => {
// State
@@ -96,11 +94,11 @@ const MarketingProductForm = ({
};
// Formik
- const formik = useFormik({
+ const formik = useFormik({
enableReinitialize: true,
initialValues: {
vehicle_number:
- initialValues?.marketing_delivery_products?.vehicle_number || undefined,
+ initialValues?.delivery_product?.vehicle_number || undefined,
kandang_id: initialValues?.product_warehouse.warehouse.id || undefined,
kandang: {
value: initialValues?.product_warehouse.warehouse.id as number,
@@ -115,15 +113,10 @@ const MarketingProductForm = ({
unit_price: initialValues?.unit_price || undefined,
total_weight: initialValues?.total_weight || undefined,
qty: initialValues?.qty || undefined,
- uom: initialValues?.product_warehouse?.product?.uom?.name || undefined,
avg_weight: initialValues?.avg_weight || undefined,
total_price: initialValues?.total_price || undefined,
- delivery_date:
- initialValues?.marketing_delivery_products?.delivery_date ||
- new Date().toDateString() ||
- undefined,
},
- validationSchema: MarketingProductSchema,
+ validationSchema: SalesOrderProductSchema,
onSubmit: async (values) => {
setFormErrorMessage('');
if (
@@ -137,7 +130,7 @@ const MarketingProductForm = ({
(item: Kandang) => item.id === values.kandang_id
);
- const marketingProduct: CreateMarketingProductPayload = {
+ const marketingProduct: CreateSalesOrderProductPayload = {
id: initialValues?.id || undefined,
vehicle_number: formatVechicleNumber(values.vehicle_number as string),
kandang_id: values.kandang_id as number,
@@ -147,10 +140,8 @@ const MarketingProductForm = ({
unit_price: values.unit_price as number,
total_weight: values.total_weight as number,
qty: values.qty as number,
- uom: values.uom as string,
avg_weight: values.avg_weight as number,
total_price: values.total_price as number,
- delivery_date: values.delivery_date as string,
};
onSubmitForm?.(marketingProduct, values);
@@ -178,10 +169,8 @@ const MarketingProductForm = ({
unit_price: '',
total_weight: '',
qty: '',
- uom: '',
avg_weight: '',
total_price: '',
- delivery_date: new Date().toDateString(),
},
});
};
diff --git a/src/config/approval-line.ts b/src/config/approval-line.ts
index 52e4290f..997d473f 100644
--- a/src/config/approval-line.ts
+++ b/src/config/approval-line.ts
@@ -32,3 +32,18 @@ export const TRANSFER_TO_LAYING_APPROVAL_LINE: ApprovalLine = [
step_name: 'Disetujui',
},
] as const;
+
+export const MARKETING_APPROVAL_LINE: ApprovalLine = [
+ {
+ step_number: 1,
+ step_name: 'Pengajuan',
+ },
+ {
+ step_number: 2,
+ step_name: 'Sales Order',
+ },
+ {
+ step_number: 3,
+ step_name: 'Delivery Order',
+ },
+];
diff --git a/src/dummy/marketing.dummy.ts b/src/dummy/marketing.dummy.ts
index 7cbf4317..1d6dde87 100644
--- a/src/dummy/marketing.dummy.ts
+++ b/src/dummy/marketing.dummy.ts
@@ -176,8 +176,7 @@ export const dummyMarketings: Marketing[] = [
{
id: 1,
status: 'APPROVED',
- so_number: 'SO-001-2025',
- so_docs: 'https://example.com/docs/so001.pdf',
+ name: 'SO-001-2025',
so_date: format(new Date(), 'yyyy-MM-dd'),
customer: {
id: 1,
@@ -193,9 +192,8 @@ export const dummyMarketings: Marketing[] = [
created_at: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
updated_at: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
},
- sales_person: createdUser,
+ sales_person_id: createdUser.id,
notes: 'Pengiriman awal bulan.',
- grand_total: 7500000,
approval: {
step_number: 1,
step_name: 'Pengajuan Order',
@@ -212,7 +210,7 @@ export const dummyMarketings: Marketing[] = [
total_weight: 250,
total_price: 7500000,
product_warehouse: dummyProductWarehouses[0],
- marketing_delivery_products: {
+ delivery_product: {
id: 1,
qty: 100,
unit_price: 75000,
@@ -233,8 +231,7 @@ export const dummyMarketings: Marketing[] = [
{
id: 2,
status: 'APPROVED',
- so_number: 'SO-002-2025',
- so_docs: 'https://example.com/docs/so002.pdf',
+ name: 'SO-002-2025',
so_date: format(new Date(), 'yyyy-MM-dd'),
customer: {
id: 2,
@@ -250,9 +247,8 @@ export const dummyMarketings: Marketing[] = [
created_at: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
updated_at: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
},
- sales_person: createdUser,
+ sales_person_id: createdUser.id,
notes: 'Pesanan kedua untuk stok akhir tahun.',
- grand_total: 3750000,
approval: {
step_number: 2,
step_name: 'Sales Order',
@@ -269,7 +265,7 @@ export const dummyMarketings: Marketing[] = [
total_weight: 125,
total_price: 3750000,
product_warehouse: dummyProductWarehouses[1],
- marketing_delivery_products: {
+ delivery_product: {
id: 2,
qty: 50,
unit_price: 75000,
@@ -290,8 +286,7 @@ export const dummyMarketings: Marketing[] = [
{
id: 3,
status: 'APPROVED',
- so_number: 'SO-003-2025',
- so_docs: 'https://example.com/docs/so003.pdf',
+ name: 'SO-003-2025',
so_date: format(new Date(), 'yyyy-MM-dd'),
customer: {
id: 3,
@@ -307,9 +302,8 @@ export const dummyMarketings: Marketing[] = [
created_at: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
updated_at: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
},
- sales_person: createdUser,
+ sales_person_id: createdUser.id,
notes: 'Order untuk pengiriman ke luar kota.',
- grand_total: 5600000,
approval: {
step_number: 3,
step_name: 'Delivery Order',
@@ -326,7 +320,7 @@ export const dummyMarketings: Marketing[] = [
total_weight: 192,
total_price: 5600000,
product_warehouse: dummyProductWarehouses[0],
- marketing_delivery_products: {
+ delivery_product: {
id: 3,
qty: 80,
unit_price: 70000,
@@ -345,7 +339,7 @@ export const dummyMarketings: Marketing[] = [
total_weight: 192,
total_price: 5600000,
product_warehouse: dummyProductWarehouses[0],
- marketing_delivery_products: {
+ delivery_product: {
id: 3,
qty: 80,
unit_price: 70000,
diff --git a/src/lib/helper.ts b/src/lib/helper.ts
index 777fa6dd..d210c646 100644
--- a/src/lib/helper.ts
+++ b/src/lib/helper.ts
@@ -32,7 +32,7 @@ export const formatNumber = (
export function formatVechicleNumber(value: string): string {
let result = '';
- for (let i = 0; i < value.length; i++) {
+ for (let i = 0; i < (value?.length ?? 0); i++) {
const curr = value[i];
const prev = value[i - 1];
diff --git a/src/services/api/marketing/marketing.ts b/src/services/api/marketing/marketing.ts
index a5c3b8fb..48435716 100644
--- a/src/services/api/marketing/marketing.ts
+++ b/src/services/api/marketing/marketing.ts
@@ -5,82 +5,25 @@ import { httpClient } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general';
import {
Marketing,
- CreateMarketingPayload,
- UpdateMarketingPayload,
+ CreateSalesOrderPayload,
+ UpdateSalesOrderPayload,
} from '@/types/api/marketing/marketing';
export class MarketingService extends BaseApiService<
Marketing,
- CreateMarketingPayload,
- UpdateMarketingPayload
+ CreateSalesOrderPayload,
+ UpdateSalesOrderPayload
> {
constructor(basePath: string = '/marketing') {
super(basePath);
}
- /**
- * Override: Get all marketing data (dummy mode)
- */
- override async getAllFetcher(
- endpoint: string
- ): Promise> {
- // simulasi loading
- await sleep(750);
-
- // data dummy sementara
- const DUMMY_MARKETING_DATA: BaseApiResponse = {
- code: 200,
- status: 'success',
- message: 'Berhasil mengambil data marketing (dummy)',
- data: dummyMarketings,
- };
-
- return DUMMY_MARKETING_DATA;
- }
-
- /**
- * Override: Get single marketing data (dummy mode)
- */
- override async getSingle(
- id: number
- ): Promise | undefined> {
- // simulasi delay
- await new Promise((res) => setTimeout(res, 500));
-
- const marketing = dummyMarketings.find((marketing) => {
- console.log('marketing', marketing);
- console.log('id-m', marketing.id);
- console.log('id-p', id);
- console.log('id', marketing.id == id);
- return marketing.id == id;
- });
- console.log('marketings', dummyMarketings);
- console.log('marketing', marketing);
-
- if (marketing) {
- // misalnya fetch dari dummy
- return {
- code: 200,
- status: 'success',
- message: 'Data marketing berhasil diambil.',
- data: marketing,
- };
- } else {
- // jika tidak ditemukan
- throw {
- code: 404,
- status: 'error',
- message: 'Data marketing tidak ditemukan.',
- };
- }
- }
-
/**
* Approve single marketing data
*/
async singleApproval(
id: number,
- action: 'approve' | 'reject'
+ action: 'APPROVED' | 'REJECTED'
): Promise | undefined> {
try {
const path = `${this.basePath}/approvals`;
@@ -88,7 +31,7 @@ export class MarketingService extends BaseApiService<
method: 'POST',
body: {
action: action,
- approval_ids: [id],
+ approvable_ids: [id],
notes: `${action} marketing ${id}`,
},
});
@@ -103,7 +46,7 @@ export class MarketingService extends BaseApiService<
*/
async bulkApprovals(
ids: number[],
- action: 'approve' | 'reject'
+ action: 'APPROVED' | 'REJECTED'
): Promise | undefined> {
try {
const path = `${this.basePath}/approvals`;
@@ -111,7 +54,7 @@ export class MarketingService extends BaseApiService<
method: 'POST',
body: {
action: action,
- approval_ids: ids,
+ approvable_ids: ids,
notes: `${action} marketing ${ids.join(', ')}`,
},
});
@@ -122,4 +65,4 @@ export class MarketingService extends BaseApiService<
}
}
-export const MarketingApi = new MarketingService('/marketing');
+export const MarketingApi = new MarketingService('/marketing/sales-orders');
diff --git a/src/types/api/marketing/marketing.d.ts b/src/types/api/marketing/marketing.d.ts
index 331d95d3..aaa0bdf0 100644
--- a/src/types/api/marketing/marketing.d.ts
+++ b/src/types/api/marketing/marketing.d.ts
@@ -7,16 +7,17 @@ import {
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
import { Kandang } from '@/types/api/master-data/kandang';
+/**
+ * Base Data Response
+ */
export type BaseMarketing = {
id: number;
status?: string;
- so_number: string;
+ name: string;
customer: Customer;
- so_docs: string;
so_date: string;
- sales_person: CreatedUser;
+ sales_person_id: number;
notes: string;
- grand_total: number;
approval: BaseApproval;
marketing_products?: MarketingProduct[];
};
@@ -29,7 +30,7 @@ export type MarketingProduct = {
total_weight: number;
total_price: number;
product_warehouse: ProductWarehouse;
- marketing_delivery_products?: MarketingDeliveryProducts;
+ delivery_product?: MarketingDeliveryProducts;
};
export type MarketingDeliveryProducts = {
@@ -39,34 +40,49 @@ export type MarketingDeliveryProducts = {
avg_weight: number;
total_weight: number;
total_price: number;
- delivery_date: string;
+ delivery_date: string | null;
vehicle_number: string;
- do_number?: string | undefined;
+ do_number?: string | undefined; // Uncertain
};
export type Marketing = BaseMetadata & BaseMarketing;
-export type CreateMarketingPayload = {
+/**
+ * Base Data Payload
+ */
+export type BaseCreateMarketingPayload = {
customer_id: number;
+ sales_person_id: number;
date: string;
notes: string;
- marketing_products: CreateMarketingProductPayload[];
};
-export type UpdateMarketingPayload = CreateMarketingPayload;
-export type CreateMarketingProductPayload = {
- id?: number;
+export type BaseCreateMarketingProductPayload = {
vehicle_number: string;
kandang_id: string | number | undefined;
- kandang: Kandang | undefined;
product_warehouse_id: string | number | undefined;
- product_warehouse: ProductWarehouse | undefined;
unit_price: string | number | undefined;
total_weight: string | number | undefined;
qty: string | number | undefined;
- uom: string | undefined;
avg_weight: string | number | undefined;
total_price: string | number | undefined;
- delivery_date?: string | null;
};
-export type UpdateMarketingProductPayload = CreateMarketingProductPayload;
+
+/**
+ * Payload Data Types Sales Order
+ */
+
+export type CreateSalesOrderPayload = BaseCreateMarketingPayload & {
+ marketing_products: CreateSalesOrderProductPayload[];
+};
+
+export type CreateSalesOrderProductPayload =
+ BaseCreateMarketingProductPayload & {
+ id?: number;
+ kandang?: Kandang | undefined;
+ product_warehouse?: ProductWarehouse | undefined;
+ };
+
+export type UpdateSalesOrderProductPayload = CreateSalesOrderProductPayload;
+
+export type UpdateSalesOrderPayload = CreateSalesOrderPayload;