diff --git a/src/components/pages/expense/ExpenseRealizationContent.tsx b/src/components/pages/expense/ExpenseRealizationContent.tsx index c69f089f..ccd57ec3 100644 --- a/src/components/pages/expense/ExpenseRealizationContent.tsx +++ b/src/components/pages/expense/ExpenseRealizationContent.tsx @@ -16,7 +16,7 @@ import { } from '@/components/pages/expense/form/ExpenseRequestForm.schema'; import { ExpenseApi } from '@/services/api/expense'; import { isResponseSuccess } from '@/lib/api-helper'; -import { ACCEPTED_FILE_TYPE } from '@/config/constant'; +import { ACCEPTED_FILE_TYPE, S3_PUBLIC_BASE_URL } from '@/config/constant'; interface ExpenseRealizationContentProps { initialValues?: Expense; @@ -103,24 +103,32 @@ const ExpenseRealizationContent = ({ initialValues?.realization_docs.length > 0 && ( )} @@ -211,7 +219,7 @@ const ExpenseRealizationContent = ({ let expenseGrandTotal = 0; kandangExpense.pengajuans?.forEach( - (item) => (expenseGrandTotal += item.price) + (item) => (expenseGrandTotal += item.qty * item.price) ); return ( @@ -273,7 +281,7 @@ const ExpenseRealizationContent = ({ let expenseGrandTotal = 0; kandangExpense.realisasi?.forEach( - (item) => (expenseGrandTotal += item.price) + (item) => (expenseGrandTotal += item.qty * item.price) ); return ( diff --git a/src/components/pages/expense/ExpenseRequestContent.tsx b/src/components/pages/expense/ExpenseRequestContent.tsx index b937c5bc..2b9086e0 100644 --- a/src/components/pages/expense/ExpenseRequestContent.tsx +++ b/src/components/pages/expense/ExpenseRequestContent.tsx @@ -27,7 +27,7 @@ import { UploadRequestDocumentsFormSchema, UploadRequestDocumentsFormValues, } from '@/components/pages/expense/form/ExpenseRequestForm.schema'; -import { ACCEPTED_FILE_TYPE } from '@/config/constant'; +import { ACCEPTED_FILE_TYPE, S3_PUBLIC_BASE_URL } from '@/config/constant'; import { ExpenseApi } from '@/services/api/expense'; import { isResponseSuccess } from '@/lib/api-helper'; import { EXPENSE_REQUEST_APPROVAL_LINE } from '@/config/approval-line'; @@ -408,9 +408,13 @@ const ExpenseRequestContent = ({ Kandang : - {initialValues?.kandangs - .map((item) => item.name) - .join(', ')} + {initialValues?.kandangs && + initialValues?.kandangs.some((k) => k.name) + ? initialValues?.kandangs + .filter((item) => item.name) + .map((item) => item.name) + .join(', ') + : '-'} @@ -448,7 +452,14 @@ const ExpenseRequestContent = ({ Nominal Biaya : - {formatCurrency(initialValues?.grand_total ?? 0)} + + {formatCurrency( + initialValues?.latest_approval.step_number === 4 || + initialValues?.latest_approval.step_number === 5 + ? (initialValues?.total_realisasi ?? 0) + : (initialValues?.total_pengajuan ?? 0) + )} + Status Pencairan @@ -482,24 +493,32 @@ const ExpenseRequestContent = ({ initialValues?.documents.length > 0 && ( )} @@ -558,7 +577,7 @@ const ExpenseRequestContent = ({ let expenseGrandTotal = 0; kandangExpense.pengajuans?.forEach( - (item) => (expenseGrandTotal += item.price) + (item) => (expenseGrandTotal += item.qty * item.price) ); return ( @@ -573,7 +592,9 @@ const ExpenseRequestContent = ({ colSpan={5} className='font-bold text-center text-base-content text-lg' > - Biaya {kandangExpense.name} + {kandangExpense.kandang_id && kandangExpense.name + ? `Biaya ${kandangExpense.name}` + : `Biaya ${initialValues?.location.name || 'Umum'}`} diff --git a/src/components/pages/expense/form/ExpenseKandangsTable.tsx b/src/components/pages/expense/form/ExpenseKandangsTable.tsx index b3c9f46d..7d7f76ca 100644 --- a/src/components/pages/expense/form/ExpenseKandangsTable.tsx +++ b/src/components/pages/expense/form/ExpenseKandangsTable.tsx @@ -20,10 +20,10 @@ interface ExpenseKandangsTableProps { locationId?: number; type: 'add' | 'edit' | 'detail'; selectedKandangs: { - id: number; - name: string; + id?: number; + name?: string; }[]; - onChange: (kandangs: { id: number; name: string }[]) => void; + onChange: (kandangs: { id?: number; name?: string }[]) => void; className?: { wrapper?: string; }; @@ -67,7 +67,11 @@ const ExpenseKandangsTable = ({ ); const [sorting, setSorting] = useState([]); const [rowSelection, setRowSelection] = useState>( - convertRowSelectionArrToObj(selectedKandangs.map((item) => item.id)) + convertRowSelectionArrToObj( + selectedKandangs + .map((item) => item.id) + .filter((id): id is number => id !== undefined) + ) ); const kandangsColumns: ColumnDef[] = [ diff --git a/src/components/pages/expense/form/ExpenseRealizationForm.schema.ts b/src/components/pages/expense/form/ExpenseRealizationForm.schema.ts index 77db761c..1f3682ea 100644 --- a/src/components/pages/expense/form/ExpenseRealizationForm.schema.ts +++ b/src/components/pages/expense/form/ExpenseRealizationForm.schema.ts @@ -1,6 +1,7 @@ import * as Yup from 'yup'; import { Expense } from '@/types/api/expense'; import { formatDate } from '@/lib/helper'; +import { S3_PUBLIC_BASE_URL } from '@/config/constant'; type ExpenseRealizationFormSchemaType = { category?: { @@ -12,7 +13,7 @@ type ExpenseRealizationFormSchemaType = { label: string; }; realization_date?: string; - kandangs?: { id: number; name: string }[]; + kandangs?: { id?: number; name?: string }[]; supplier?: { value: number; label: string; @@ -20,7 +21,7 @@ type ExpenseRealizationFormSchemaType = { existing_documents?: { name: string; url: string }[]; documents?: File[]; realizations: { - kandang_id: number; + kandang_id?: number; cost_items: { nonstock?: { value: number; @@ -49,12 +50,11 @@ export const ExpenseRealizationFormSchema: Yup.ObjectSchema ({ - name: doc.path, - url: doc.path, - })), + existing_documents: initialValues?.realization_docs?.map((doc) => { + const path = doc.path.startsWith('/') ? doc.path.slice(1) : doc.path; + return { + name: doc.path, + url: `${S3_PUBLIC_BASE_URL}/${path}`, + }; + }), documents: [], realizations: initialValues?.kandangs ? initialValues.kandangs.map((kandangExpense) => { diff --git a/src/components/pages/expense/form/ExpenseRealizationForm.tsx b/src/components/pages/expense/form/ExpenseRealizationForm.tsx index d1c7c5f2..6526b1c1 100644 --- a/src/components/pages/expense/form/ExpenseRealizationForm.tsx +++ b/src/components/pages/expense/form/ExpenseRealizationForm.tsx @@ -150,25 +150,10 @@ const ExpenseRealizationForm = ({ formik.setFieldValue('location', val); formik.setFieldValue('kandangs', []); - formik.setFieldValue('realizations', []); - }; - const kandangsChangeHandler = (kandangs: { id: number; name: string }[]) => { - formik.setFieldTouched('kandangs', true); - formik.setFieldValue('kandangs', kandangs); - - const newRealizations = [...(formik.values.realizations ?? [])]; - - // add new realizations - kandangs.forEach((kandangItem) => { - const isKandangExistInRealization = newRealizations.find( - (realizationItem) => realizationItem.kandang_id === kandangItem.id - ); - - if (isKandangExistInRealization) return; - - newRealizations.push({ - kandang_id: kandangItem.id, + // Auto-create realization item for location (without kandang) + formik.setFieldValue('realizations', [ + { cost_items: [ { nonstock: undefined, @@ -177,25 +162,57 @@ const ExpenseRealizationForm = ({ notes: '', }, ], + }, + ]); + }; + + const kandangsChangeHandler = ( + kandangs: { id?: number; name?: string }[] + ) => { + formik.setFieldTouched('kandangs', true); + formik.setFieldValue('kandangs', kandangs); + + // If no kandangs selected, create realization item for location + if (kandangs.length === 0) { + formik.setFieldValue('realizations', [ + { + cost_items: [ + { + nonstock: undefined, + quantity: undefined, + price: undefined, + notes: '', + }, + ], + }, + ]); + return; + } + + // Start with empty array when kandangs are selected + const newRealizations: typeof formik.values.realizations = []; + + // add new realizations for each kandang + kandangs.forEach((kandangItem) => { + if (!kandangItem.id) return; + + const existingRealization = formik.values.realizations?.find( + (realizationItem) => realizationItem.kandang_id === kandangItem.id + ); + + newRealizations.push({ + kandang_id: kandangItem.id, + cost_items: existingRealization?.cost_items || [ + { + nonstock: undefined, + quantity: undefined, + price: undefined, + notes: '', + }, + ], }); }); - // prune realizations - const kandangIds = new Set(kandangs.map((kandang) => kandang.id)); - const deletedRealizationsIdx: number[] = []; - - newRealizations.forEach((realization, idx) => { - const isRealizationValid = kandangIds.has(realization.kandang_id); - - if (!isRealizationValid) { - deletedRealizationsIdx.push(idx); - } - }); - - deletedRealizationsIdx.forEach((deletedRealizationIdx) => { - newRealizations.splice(deletedRealizationIdx, 1); - }); - formik.setFieldValue('realizations', newRealizations); }; @@ -338,7 +355,10 @@ const ExpenseRealizationForm = ({ )} ; + supplierId?: number; + location?: { + value: number; + label: string; + }; className?: { wrapper?: string; }; @@ -25,12 +30,18 @@ interface ExpenseRealizationKandangDetailExpenseProps { const ExpenseRealizationKandangDetailExpense: React.FC< ExpenseRealizationKandangDetailExpenseProps -> = ({ type, formik, className }) => { +> = ({ type, formik, supplierId, location, className }) => { const { setInputValue: setNonstockInputValue, options: nonstockOptions, isLoadingOptions: isLoadingNonstockOptions, - } = useSelect(NonstockApi.basePath, 'id', 'name'); + } = useSelect( + NonstockApi.basePath, + 'id', + 'name', + 'search', + supplierId ? { supplier_id: String(supplierId) } : undefined + ); const nonstockChangeHandler = ( kandangExpenseIdx: number, @@ -82,140 +93,159 @@ const ExpenseRealizationKandangDetailExpense: React.FC<
- {formik.values.realizations.length === 0 && ( + {!formik.values.supplier?.value && (

- Pilih kandang terlebih dahulu! + Pilih supplier terlebih dahulu!

)} - {formik.values.realizations.map((kandangExpense, kandangExpenseIdx) => { - const kandangName = formik.values.kandangs?.find( - (kandang) => kandang.id === kandangExpense.kandang_id - ); + {formik.values.realizations.length === 0 && + formik.values.supplier?.value && ( +
+

+ Belum ada item biaya. Silakan pilih lokasi terlebih dahulu. +

+
+ )} - return ( - kandangName?.name && ( -
-
-
- Biaya {kandangName?.name} -
+ {formik.values.realizations.length > 0 && + formik.values.supplier?.value && + formik.values.realizations.map( + (kandangExpense, kandangExpenseIdx) => { + const kandangName = kandangExpense.kandang_id + ? formik.values.kandangs?.find( + (kandang) => kandang.id === kandangExpense.kandang_id + ) + : null; -
- - - - - - - - - + return ( + (kandangName?.name || !kandangExpense.kandang_id) && ( +
+
+
+ {kandangName?.name + ? `Biaya ${kandangName.name}` + : location?.label + ? `Biaya ${location.label}` + : 'Biaya Umum'} +
-
- {kandangExpense.cost_items.map( - (expenseItem, expenseIdx) => ( - - - - - - - - +
+
NonstockTotal KuantitasHarga SatuanCatatan
- { - nonstockChangeHandler( - kandangExpenseIdx, - expenseIdx, - val - ); - }} - options={nonstockOptions} - isLoading={isLoadingNonstockOptions} - onInputChange={setNonstockInputValue} - className={{ wrapper: 'min-w-48' }} - isDisabled - /> - - - - - Rp - - } - className={{ wrapper: 'min-w-24' }} - /> - - -
+ + + + + + - ) - )} - -
NonstockTotal KuantitasHarga SatuanCatatan
+ + + + {kandangExpense.cost_items.map( + (expenseItem, expenseIdx) => ( + + + { + nonstockChangeHandler( + kandangExpenseIdx, + expenseIdx, + val + ); + }} + options={nonstockOptions} + isLoading={isLoadingNonstockOptions} + onInputChange={setNonstockInputValue} + className={{ wrapper: 'min-w-48' }} + isDisabled + /> + + + + + + + + + Rp + + } + className={{ wrapper: 'min-w-24' }} + /> + + + + + + + ) + )} + + +
+
-
- - ) - ); - })} + ) + ); + } + )} ); diff --git a/src/components/pages/expense/form/ExpenseRequestForm.schema.ts b/src/components/pages/expense/form/ExpenseRequestForm.schema.ts index 7758df83..71357361 100644 --- a/src/components/pages/expense/form/ExpenseRequestForm.schema.ts +++ b/src/components/pages/expense/form/ExpenseRequestForm.schema.ts @@ -1,6 +1,7 @@ import * as Yup from 'yup'; import { Expense } from '@/types/api/expense'; import { formatDate } from '@/lib/helper'; +import { S3_PUBLIC_BASE_URL } from '@/config/constant'; type ExpenseFormSchemaType = { category?: { @@ -11,8 +12,9 @@ type ExpenseFormSchemaType = { value: number; label: string; }; + location_id: number; transaction_date?: string; - kandangs?: { id: number; name: string }[]; + kandangs?: { id?: number; name?: string }[]; supplier?: { value: number; label: string; @@ -21,7 +23,7 @@ type ExpenseFormSchemaType = { deleted_documents?: number[]; documents?: File[]; expense_nonstocks: { - kandang_id: number; + kandang_id?: number; cost_items: { nonstock?: { value: number; @@ -46,16 +48,17 @@ export const ExpenseRequestFormSchema: Yup.ObjectSchema = label: Yup.string().required(), }).required('Lokasi wajib diisi!'), + location_id: Yup.number().min(1).required('Lokasi wajib diisi!'), + transaction_date: Yup.string().required('Tanggal transaksi wajib diisi!'), kandangs: Yup.array() .of( Yup.object({ - id: Yup.number().required('Kandang wajib dipilih!'), - name: Yup.string().required('Kandang wajib dipilih!'), + id: Yup.number().optional(), + name: Yup.string().optional(), }) ) - .min(1, 'Kandang wajib dipilih!') - .required('Kandang wajib dipilih!'), + .optional(), supplier: Yup.object({ value: Yup.number().min(1).required(), @@ -77,7 +80,7 @@ export const ExpenseRequestFormSchema: Yup.ObjectSchema = expense_nonstocks: Yup.array() .of( Yup.object({ - kandang_id: Yup.number().min(1, 'Wajib memilih kandang!').required(), + kandang_id: Yup.number().min(1, 'Wajib memilih kandang!').optional(), cost_items: Yup.array() .of( Yup.object({ @@ -128,6 +131,7 @@ export const getExpenseFormInitialValues = ( label: initialValues.location.name, } : undefined, + location_id: Number(initialValues?.location.id || 0), transaction_date: initialValues?.transaction_date ? formatDate(initialValues.transaction_date, 'YYYY-MM-DD') : undefined, @@ -141,11 +145,14 @@ export const getExpenseFormInitialValues = ( label: initialValues.supplier.name, } : undefined, - existing_documents: initialValues?.documents?.map((doc) => ({ - id: doc.id, - name: doc.path, - url: doc.path, - })), + existing_documents: initialValues?.documents?.map((doc) => { + const path = doc.path.startsWith('/') ? doc.path.slice(1) : doc.path; + return { + id: doc.id, + name: doc.path, + url: `${S3_PUBLIC_BASE_URL}/${path}`, + }; + }), deleted_documents: [], documents: [], expense_nonstocks: initialValues?.kandangs diff --git a/src/components/pages/expense/form/ExpenseRequestForm.tsx b/src/components/pages/expense/form/ExpenseRequestForm.tsx index 71160785..60e55397 100644 --- a/src/components/pages/expense/form/ExpenseRequestForm.tsx +++ b/src/components/pages/expense/form/ExpenseRequestForm.tsx @@ -108,18 +108,24 @@ const ExpenseRequestForm = ({ const expensePayload: CreateExpensePayload = { category: formik.values.category?.value as 'BOP' | 'NON-BOP', + location_id: values.location_id as number, transaction_date: values?.transaction_date as string, supplier_id: values.supplier?.value as number, documents: values.documents as File[], - expense_nonstocks: values.expense_nonstocks.map((expenseNonstock) => ({ - kandang_id: expenseNonstock.kandang_id, - cost_items: expenseNonstock.cost_items.map((costItem) => ({ - nonstock_id: costItem.nonstock?.value as number, - quantity: parseFloat(String(costItem.quantity)) as number, - price: parseFloat(String(costItem.price)) as number, - notes: costItem.notes ?? '', - })), - })), + expense_nonstocks: values.expense_nonstocks.map((expenseNonstock) => { + const basePayload = { + cost_items: expenseNonstock.cost_items.map((costItem) => ({ + nonstock_id: costItem.nonstock?.value as number, + quantity: parseFloat(String(costItem.quantity)) as number, + price: parseFloat(String(costItem.price)) as number, + notes: costItem.notes ?? '', + })), + }; + + return expenseNonstock.kandang_id + ? { ...basePayload, kandang_id: expenseNonstock.kandang_id } + : basePayload; + }), }; switch (type) { @@ -130,19 +136,25 @@ const ExpenseRequestForm = ({ case 'edit': const expenseUpdatePayload: UpdateExpensePayload = { category: formik.values.category?.value as 'BOP' | 'NON-BOP', + location_id: values.location_id as number, transaction_date: values?.transaction_date as string, supplier_id: values.supplier?.value as number, documents: values.documents as File[], expense_nonstocks: values.expense_nonstocks.map( - (expenseNonstock) => ({ - kandang_id: expenseNonstock.kandang_id, - cost_items: expenseNonstock.cost_items.map((costItem) => ({ - nonstock_id: costItem.nonstock?.value as number, - quantity: parseFloat(String(costItem.quantity)) as number, - price: parseFloat(String(costItem.price)) as number, - notes: costItem.notes ?? '', - })), - }) + (expenseNonstock) => { + const basePayload = { + cost_items: expenseNonstock.cost_items.map((costItem) => ({ + nonstock_id: costItem.nonstock?.value as number, + quantity: parseFloat(String(costItem.quantity)) as number, + price: parseFloat(String(costItem.price)) as number, + notes: costItem.notes ?? '', + })), + }; + + return expenseNonstock.kandang_id + ? { ...basePayload, kandang_id: expenseNonstock.kandang_id } + : basePayload; + } ), }; @@ -179,27 +191,14 @@ const ExpenseRequestForm = ({ formik.setFieldTouched('location', true); formik.setFieldValue('location', val); + const locationId = Array.isArray(val) ? val[0]?.value : val?.value; + formik.setFieldValue('location_id', locationId); + formik.setFieldValue('kandangs', []); - formik.setFieldValue('expense_nonstocks', []); - }; - const kandangsChangeHandler = (kandangs: { id: number; name: string }[]) => { - formik.setFieldTouched('kandangs', true); - formik.setFieldValue('kandangs', kandangs); - - const newExpenseNonstocks = [...(formik.values.expense_nonstocks ?? [])]; - - // add new expense_nonstocks - kandangs.forEach((kandangItem) => { - const isKandangExistInExpenseNonstocks = newExpenseNonstocks.find( - (expenseNonstockItem) => - expenseNonstockItem.kandang_id === kandangItem.id - ); - - if (isKandangExistInExpenseNonstocks) return; - - newExpenseNonstocks.push({ - kandang_id: kandangItem.id, + // Auto-create expense item for location (without kandang) + formik.setFieldValue('expense_nonstocks', [ + { cost_items: [ { nonstock: undefined, @@ -208,25 +207,56 @@ const ExpenseRequestForm = ({ notes: '', }, ], + }, + ]); + }; + + const kandangsChangeHandler = ( + kandangs: { id?: number; name?: string }[] + ) => { + formik.setFieldTouched('kandangs', true); + formik.setFieldValue('kandangs', kandangs); + + // If no kandangs selected, create expense item for location + if (kandangs.length === 0) { + formik.setFieldValue('expense_nonstocks', [ + { + cost_items: [ + { + nonstock: undefined, + quantity: undefined, + price: undefined, + notes: '', + }, + ], + }, + ]); + return; + } + + const newExpenseNonstocks: typeof formik.values.expense_nonstocks = []; + + kandangs.forEach((kandangItem) => { + if (!kandangItem.id) return; + + const existingExpenseNonstock = formik.values.expense_nonstocks?.find( + (expenseNonstockItem) => + expenseNonstockItem.kandang_id === kandangItem.id + ); + + newExpenseNonstocks.push({ + kandang_id: kandangItem.id, + cost_items: existingExpenseNonstock?.cost_items || [ + { + nonstock: undefined, + quantity: undefined, + price: undefined, + notes: '', + }, + ], }); }); - // prune expense_nonstocks - const kandangIds = new Set(kandangs.map((kandang) => kandang.id)); - const deletedExpenseNonstocksIdx: number[] = []; - - newExpenseNonstocks.forEach((expenseNonstock, idx) => { - const isExpenseNonstockValid = kandangIds.has(expenseNonstock.kandang_id); - - if (!isExpenseNonstockValid) { - deletedExpenseNonstocksIdx.push(idx); - } - }); - - deletedExpenseNonstocksIdx.forEach((deletedExpenseNonstockIdx) => { - newExpenseNonstocks.splice(deletedExpenseNonstockIdx, 1); - }); - formik.setFieldValue('expense_nonstocks', newExpenseNonstocks); }; @@ -454,7 +484,10 @@ const ExpenseRequestForm = ({ )} ; + supplierId?: number; + location?: { + value: number; + label: string; + }; className?: { wrapper?: string; }; @@ -28,12 +33,18 @@ interface ExpenseRequestKandangDetailExpenseProps { const ExpenseRequestKandangDetailExpense: React.FC< ExpenseRequestKandangDetailExpenseProps -> = ({ type, formik, className }) => { +> = ({ type, formik, supplierId, location, className }) => { const { setInputValue: setNonstockInputValue, options: nonstockOptions, isLoadingOptions: isLoadingNonstockOptions, - } = useSelect(NonstockApi.basePath, 'id', 'name'); + } = useSelect( + NonstockApi.basePath, + 'id', + 'name', + 'search', + supplierId ? { supplier_id: String(supplierId) } : undefined + ); const nonstockChangeHandler = ( kandangExpenseIdx: number, @@ -113,41 +124,57 @@ const ExpenseRequestKandangDetailExpense: React.FC<
- {(formik.values.expense_nonstocks.length === 0 || - !formik.values.supplier?.value) && ( + {!formik.values.supplier?.value && (

- Pilih kandang terlebih dahulu! + Pilih supplier terlebih dahulu!

)} + {formik.values.expense_nonstocks.length === 0 && + formik.values.supplier?.value && ( +
+

+ Belum ada item biaya. Silakan pilih lokasi terlebih dahulu. +

+
+ )} + {formik.values.expense_nonstocks.length > 0 && formik.values.supplier?.value && formik.values.expense_nonstocks.map( (kandangExpense, kandangExpenseIdx) => { - const kandangName = formik.values.kandangs?.find( - (kandang) => kandang.id === kandangExpense.kandang_id - ); + const kandangName = kandangExpense.kandang_id + ? formik.values.kandangs?.find( + (kandang) => kandang.id === kandangExpense.kandang_id + ) + : null; return ( - kandangName?.name && ( + (kandangName?.name || !kandangExpense.kandang_id) && (
- Biaya {kandangName?.name} + Biaya {kandangName?.name || location?.label || 'Umum'}
- - - + + + {type !== 'detail' && } diff --git a/src/components/pages/expense/pdf/ExpensePDF.tsx b/src/components/pages/expense/pdf/ExpensePDF.tsx index 5b107127..ef1c7d8b 100644 --- a/src/components/pages/expense/pdf/ExpensePDF.tsx +++ b/src/components/pages/expense/pdf/ExpensePDF.tsx @@ -219,7 +219,13 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => { { label: 'Lokasi', value: expense?.location.name }, { label: 'Kandang', - value: expense?.kandangs.map((item) => item.name).join(', '), + value: + expense?.kandangs && expense?.kandangs.some((k) => k.name) + ? expense?.kandangs + .filter((item) => item.name) + .map((item) => item.name) + .join(', ') + : '-', }, { label: 'Vendor', value: expense?.supplier.name }, { @@ -235,7 +241,12 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => { { label: 'Nama Pengaju', value: expense?.created_user.name }, { label: 'Nominal Biaya', - value: formatCurrency(expense?.grand_total ?? 0), + value: formatCurrency( + expense?.latest_approval.step_number === 4 || + expense?.latest_approval.step_number === 5 + ? (expense?.total_realisasi ?? 0) + : (expense?.total_pengajuan ?? 0) + ), }, { label: 'Nominal Pengajuan', @@ -326,7 +337,7 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => { let expenseRequestTotal = 0; kandangExpense.pengajuans?.forEach( - (item) => (expenseRequestTotal += item.price) + (item) => (expenseRequestTotal += item.qty * item.price) ); return ( @@ -335,7 +346,9 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => { style={ExpensePDFStyle.kandangExpenseContainer} > - {kandangExpense.name} + {kandangExpense.kandang_id && kandangExpense.name + ? `Biaya ${kandangExpense.name}` + : `Biaya ${expense?.location.name || 'Umum'}`} @@ -484,7 +497,7 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => { let expenseRealizationTotal = 0; kandangExpense.realisasi?.forEach( - (item) => (expenseRealizationTotal += item.price) + (item) => (expenseRealizationTotal += item.qty * item.price) ); return ( @@ -493,7 +506,9 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => { style={ExpensePDFStyle.kandangExpenseContainer} > - {kandangExpense.name} + {kandangExpense.kandang_id && kandangExpense.name + ? `Biaya ${kandangExpense.name}` + : `Biaya ${expense?.location.name || 'Umum'}`} diff --git a/src/config/constant.ts b/src/config/constant.ts index 48e5a435..01ee6ed9 100644 --- a/src/config/constant.ts +++ b/src/config/constant.ts @@ -352,6 +352,9 @@ export const ACCEPTED_FILE_TYPE = { }, }; +export const S3_PUBLIC_BASE_URL = process.env + .NEXT_PUBLIC_S3_PUBLIC_BASE_URL as string; + export const FILTER_TYPE_OPTIONS = [ { label: 'Tanggal Realisasi', diff --git a/src/services/api/expense.ts b/src/services/api/expense.ts index 44a855f4..70e0e339 100644 --- a/src/services/api/expense.ts +++ b/src/services/api/expense.ts @@ -483,6 +483,7 @@ export class ExpenseApiService extends BaseApiService< const formData = new FormData(); formData.append('category', payload.category); + formData.append('location_id', String(payload.location_id)); formData.append('transaction_date', payload.transaction_date); formData.append('supplier_id', String(payload.supplier_id)); @@ -505,6 +506,7 @@ export class ExpenseApiService extends BaseApiService< const formData = new FormData(); formData.append('category', payload.category); + formData.append('location_id', String(payload.location_id)); formData.append('transaction_date', payload.transaction_date); formData.append('supplier_id', String(payload.supplier_id)); diff --git a/src/types/api/expense.d.ts b/src/types/api/expense.d.ts index a62066ba..12455cc8 100644 --- a/src/types/api/expense.d.ts +++ b/src/types/api/expense.d.ts @@ -57,11 +57,12 @@ export type Expense = BaseMetadata & BaseExpense; export type CreateExpensePayload = { category: 'BOP' | 'NON-BOP'; + location_id: number; transaction_date: string; supplier_id: number; documents: File[]; expense_nonstocks: { - kandang_id: number; + kandang_id?: number; cost_items: { nonstock_id: number; quantity: number; @@ -72,12 +73,13 @@ export type CreateExpensePayload = { }; export type UpdateExpensePayload = { + location_id: number; category: 'BOP' | 'NON-BOP'; transaction_date: string; supplier_id: number; documents: File[]; expense_nonstocks: { - kandang_id: number; + kandang_id?: number; cost_items: { nonstock_id: number; quantity: number;
NonstockTotal KuantitasHarga Satuan + Nonstock + + Total Kuantitas + + Harga Satuan + CatatanAksi