diff --git a/src/components/Expenses/ExpenseList.jsx b/src/components/Expenses/ExpenseList.jsx
index a56d340b..474fa374 100644
--- a/src/components/Expenses/ExpenseList.jsx
+++ b/src/components/Expenses/ExpenseList.jsx
@@ -298,7 +298,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
)
)}
-
+
diff --git a/src/components/Expenses/ExpenseStatusLogs.jsx b/src/components/Expenses/ExpenseStatusLogs.jsx
index 83ee2e3a..82ce8593 100644
--- a/src/components/Expenses/ExpenseStatusLogs.jsx
+++ b/src/components/Expenses/ExpenseStatusLogs.jsx
@@ -10,7 +10,7 @@ const ExpenseStatusLogs = ({ data }) => {
const sortedLogs = useMemo(() => {
if (!data?.expenseLogs) return [];
return [...data.expenseLogs].sort(
- (a, b) => new Date(b.updatedAt) - new Date(a.updatedAt)
+ (a, b) => new Date(b.updateAt) - new Date(a.updateAt)
);
}, [data?.expenseLogs]);
@@ -20,11 +20,12 @@ const ExpenseStatusLogs = ({ data }) => {
);
const timelineData = useMemo(() => {
+
return logsToShow.map((log, index) => ({
id: index + 1,
title: log.action || "Status Updated",
description: log.comment || "",
- timeAgo: log.updatedAt,
+ timeAgo: log.updateAt,
color: getColorNameFromHex(log.nextStatus?.color) || "primary",
users: log.updatedBy
? [
@@ -44,7 +45,7 @@ const ExpenseStatusLogs = ({ data }) => {
};
return (
-
+
diff --git a/src/components/Expenses/Filelist.jsx b/src/components/Expenses/Filelist.jsx
index 208a596b..51e2f9f3 100644
--- a/src/components/Expenses/Filelist.jsx
+++ b/src/components/Expenses/Filelist.jsx
@@ -13,17 +13,19 @@ const Filelist = ({ files, removeFile, expenseToEdit }) => {
return true;
})
.map((file, idx) => (
-
-
+
+
{/* File icon and info */}
-
+
-
+
{file.fileName}
@@ -32,19 +34,17 @@ const Filelist = ({ files, removeFile, expenseToEdit }) => {
-
-
- {
- e.preventDefault();
- debugger;
- removeFile(expenseToEdit ? file.documentId : idx);
- }}
- >
-
-
+ {/* Delete icon */}
+
+ {
+ e.preventDefault();
+ removeFile(expenseToEdit ? file.documentId : idx);
+ }}
+ >
+
))}
diff --git a/src/components/Expenses/ManageExpense.jsx b/src/components/Expenses/ManageExpense.jsx
index 4ee1679c..539497ca 100644
--- a/src/components/Expenses/ManageExpense.jsx
+++ b/src/components/Expenses/ManageExpense.jsx
@@ -298,35 +298,6 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
)}
-
- {/*
-
-
- {errors.paidById && (
- {errors.paidById.message}
- )}
- */}
-
{
- {formatCurrency(data.amount,data?.curency?.currencyCode)}
+ {formatFigure(data?.amount,{type:"currency",currency : data?.currency?.currencyCode ?? "INR"} )}
@@ -294,12 +295,134 @@ const ViewExpense = ({ ExpenseId }) => {
- {/* ... your remaining conditional sections */}
+ {data.expensesReimburse && (
+
+
+
+ {data.expensesReimburse.reimburseTransactionId || "N/A"}
+
+
+
+ {formatUTCToLocalTime(data.expensesReimburse.reimburseDate)}
+
+
+ {data.expensesReimburse && (
+ <>
+
+
+
+
+ {`${data?.expensesReimburse?.reimburseBy?.firstName} ${data?.expensesReimburse?.reimburseBy?.lastName}`.trim()}
+
+
+ >
+ )}
+
+ )}
+
+
+ {Array.isArray(data?.nextStatus) && data.nextStatus.length > 0 && (
+ <>
+ {IsPaymentProcess && nextStatusWithPermission?.length > 0 && (
+
+
+
+
+ {errors.reimburseTransactionId && (
+
+ {errors.reimburseTransactionId.message}
+
+ )}
+
+
+
+
+ {errors.reimburseDate && (
+
+ {errors.reimburseDate.message}
+
+ )}
+
+
+
+
+
+
+ )}
+
+ {((nextStatusWithPermission.length > 0 && !IsRejectedExpense) ||
+ (IsRejectedExpense && isCreatedBy)) && (
+ <>
+
+
+ {errors.comment && (
+
+ {errors.comment.message}
+
+ )}
+ >
+ )}
+
+ {nextStatusWithPermission?.length > 0 &&
+ (!IsRejectedExpense || isCreatedBy) && (
+
+ {nextStatusWithPermission.map((status, index) => (
+
+ ))}
+
+ )}
+
+ >
+ )}
-
+
diff --git a/src/components/PaymentRequest/MakeExpense.jsx b/src/components/PaymentRequest/MakeExpense.jsx
new file mode 100644
index 00000000..c4264fa4
--- /dev/null
+++ b/src/components/PaymentRequest/MakeExpense.jsx
@@ -0,0 +1,247 @@
+import { zodResolver } from "@hookform/resolvers/zod";
+import React from "react";
+import { useForm } from "react-hook-form";
+import {
+ DefaultRequestedExpense,
+ RequestedExpenseSchema,
+} from "./PaymentRequestSchema";
+import Label from "../common/Label";
+import { usePaymentMode } from "../../hooks/masterHook/useMaster";
+import { useCreatePaymentRequestExpense, useCreateRecurringExpense } from "../../hooks/useExpense";
+import Filelist from "../Expenses/Filelist";
+import { usePaymentRequestContext } from "../../pages/PaymentRequest/PaymentRequestPage";
+
+const MakeExpense = ({ onClose }) => {
+ const {isExpenseGenerate} = usePaymentRequestContext()
+ const {
+ PaymentModes,
+ loading: PaymentModeLoading,
+ error: PaymentModeError,
+ } = usePaymentMode();
+
+ const {
+ setValue,
+ register,
+ watch,
+ handleSubmit,
+ formState: { errors },
+ } = useForm({
+ resolver: zodResolver(RequestedExpenseSchema),
+ defaultValues: DefaultRequestedExpense,
+ });
+ const files = watch("billAttachments");
+ const onFileChange = async (e) => {
+ const newFiles = Array.from(e.target.files);
+ if (newFiles.length === 0) return;
+
+ const existingFiles = watch("billAttachments") || [];
+
+ const parsedFiles = await Promise.all(
+ newFiles.map(async (file) => {
+ const base64Data = await toBase64(file);
+ return {
+ fileName: file.name,
+ base64Data,
+ contentType: file.type,
+ fileSize: file.size,
+ description: "",
+ isActive: true,
+ };
+ })
+ );
+
+ const combinedFiles = [
+ ...existingFiles,
+ ...parsedFiles.filter(
+ (newFile) =>
+ !existingFiles.some(
+ (f) =>
+ f.fileName === newFile.fileName && f.fileSize === newFile.fileSize
+ )
+ ),
+ ];
+
+ setValue("billAttachments", combinedFiles, {
+ shouldDirty: true,
+ shouldValidate: true,
+ });
+ };
+
+ const toBase64 = (file) =>
+ new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onload = () => resolve(reader.result.split(",")[1]);
+ reader.onerror = (error) => reject(error);
+ });
+ const removeFile = (index) => {
+ debugger
+ const newFiles = files.filter((_, i) => i !== index);
+ setValue("billAttachments", newFiles, { shouldValidate: true });
+ };
+
+ const { mutate: CreatedExpense, isPending } = useCreatePaymentRequestExpense(
+ () => {
+ handleClose();
+ }
+ );
+ const onSubmit = (formData) => {
+ let payload = {
+ ...formData,
+ paymentRequestId:isExpenseGenerate?.requestId
+ }
+ CreatedExpense(payload)
+ };
+
+ const handleClose = () => {
+ onClose();
+ };
+ return (
+
+ );
+};
+
+export default MakeExpense;
diff --git a/src/components/PaymentRequest/PaymentRequestList.jsx b/src/components/PaymentRequest/PaymentRequestList.jsx
index 3e8152d5..84965427 100644
--- a/src/components/PaymentRequest/PaymentRequestList.jsx
+++ b/src/components/PaymentRequest/PaymentRequestList.jsx
@@ -279,7 +279,7 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
)
)}
-
+
diff --git a/src/components/PaymentRequest/PaymentRequestSchema.js b/src/components/PaymentRequest/PaymentRequestSchema.js
index 018b092f..7e2289fe 100644
--- a/src/components/PaymentRequest/PaymentRequestSchema.js
+++ b/src/components/PaymentRequest/PaymentRequestSchema.js
@@ -125,8 +125,43 @@ export const PaymentRequestActionScheam = (
export const defaultPaymentRequestActionValues = {
comment: "",
statusId: "",
-
paidTransactionId: null,
paidAt: null,
paidById: null,
};
+
+
+export const RequestedExpenseSchema =
+
+ z.object({
+ paymentModeId: z.string().min(1, { message: "Payment mode is required" }),
+ location: z.string().min(1, { message: "Location is required" }),
+ gstNumber: z.string().optional(),
+ billAttachments: z
+ .array(
+ z.object({
+ fileName: z.string().min(1, { message: "Filename is required" }),
+ base64Data: z.string().nullable(),
+ contentType: z
+ .string()
+ .refine((val) => ALLOWED_TYPES.includes(val), {
+ message: "Only PDF, PNG, JPG, or JPEG files are allowed",
+ }),
+ documentId: z.string().optional(),
+ fileSize: z.number().max(MAX_FILE_SIZE, {
+ message: "File size must be less than or equal to 5MB",
+ }),
+ description: z.string().optional(),
+ isActive: z.boolean().default(true),
+ })
+ )
+ .nonempty({ message: "At least one file attachment is required" }),
+ })
+
+export const DefaultRequestedExpense = {
+ paymentModeId:"",
+ location:"",
+ gstNumber:"",
+ // amount:"",
+ billAttachments:[]
+}
\ No newline at end of file
diff --git a/src/components/PaymentRequest/PaymentStatusLogs.jsx b/src/components/PaymentRequest/PaymentStatusLogs.jsx
index 9abb3fa5..1793d90e 100644
--- a/src/components/PaymentRequest/PaymentStatusLogs.jsx
+++ b/src/components/PaymentRequest/PaymentStatusLogs.jsx
@@ -20,6 +20,7 @@ const PaymentStatusLogs = ({ data }) => {
);
const timelineData = useMemo(() => {
+ console.log(logsToShow)
return logsToShow.map((log, index) => ({
id: index + 1,
title: log.nextStatus?.name || "Status Updated",
diff --git a/src/components/PaymentRequest/ViewPaymentRequest.jsx b/src/components/PaymentRequest/ViewPaymentRequest.jsx
index 0d7d280b..db8ac2e0 100644
--- a/src/components/PaymentRequest/ViewPaymentRequest.jsx
+++ b/src/components/PaymentRequest/ViewPaymentRequest.jsx
@@ -50,7 +50,7 @@ const ViewPaymentRequest = ({ requestId }) => {
const IsReview = useHasUserPermission(REVIEW_EXPENSE);
const [imageLoaded, setImageLoaded] = useState({});
- const { setDocumentView, setModalSize } = usePaymentRequestContext();
+ const { setDocumentView, setModalSize,setVieRequest ,setIsExpenseGenerate} = usePaymentRequestContext();
const ActionSchema =
PaymentRequestActionScheam(IsPaymentProcess, data?.createdAt) ??
z.object({});
@@ -118,9 +118,14 @@ const ViewPaymentRequest = ({ requestId }) => {
const handleImageLoad = (id) => {
setImageLoaded((prev) => ({ ...prev, [id]: true }));
};
+ const handleExpense = ()=>{
+
+ setIsExpenseGenerate({IsOpen:true,requestId:requestId})
+ setVieRequest({IsOpen:true,requestId:requestId})
+ }
return (
-
)}
- {Array.isArray(data?.nextStatus) && data?.nextStatus.length > 0 && (
+ {Array.isArray(data?.nextStatus) && data?.nextStatus.length > 0 ? (
<>
{IsPaymentProcess && nextStatusWithPermission?.length > 0 && (
@@ -436,11 +441,14 @@ const ViewPaymentRequest = ({ requestId }) => {
)}
>
- )}
+ ):(
+
+ )}
+
-
-
+
+
diff --git a/src/hooks/useExpense.js b/src/hooks/useExpense.js
index 181d9551..9823095f 100644
--- a/src/hooks/useExpense.js
+++ b/src/hooks/useExpense.js
@@ -395,8 +395,27 @@ export const useDeletePaymentRequest = ()=>{
},
});
}
-//#endregion
-
+export const useCreatePaymentRequestExpense = (onSuccessCallBack) => {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: async (payload) => {
+ await ExpenseRepository.CreatePaymentRequestExpense(payload);
+ },
+
+ onSuccess: (_, variables) => {
+ queryClient.invalidateQueries({ queryKey: ["Expenses"] });
+ queryClient.invalidateQueries({queryKey:["paymentRequest",variables.paymentRequestId]})
+ showToast("Expense Created Successfully", "success");
+ if (onSuccessCallBack) onSuccessCallBack();
+ },
+ onError: (error) => {
+ showToast(
+ error.message || "Something went wrong please try again !",
+ "error"
+ );
+ },
+ });
+};
export const usePaymentRequestFilter = () => {
return useQuery({
queryKey: ["PaymentRequestFilter"],
@@ -408,6 +427,9 @@ export const usePaymentRequestFilter = () => {
});
};
+//#endregion
+
+
//#region Advance Payment
export const useExpenseTransactions = (employeeId)=>{
diff --git a/src/pages/PaymentRequest/PaymentRequestPage.jsx b/src/pages/PaymentRequest/PaymentRequestPage.jsx
index 452033e6..e2f3cdf1 100644
--- a/src/pages/PaymentRequest/PaymentRequestPage.jsx
+++ b/src/pages/PaymentRequest/PaymentRequestPage.jsx
@@ -9,6 +9,7 @@ import PaymentRequestFilterPanel from "../../components/PaymentRequest/PaymentRe
import { defaultPaymentRequestFilter,SearchPaymentRequestSchema } from "../../components/PaymentRequest/PaymentRequestSchema";
import ViewPaymentRequest from "../../components/PaymentRequest/ViewPaymentRequest";
import PreviewDocument from "../../components/Expenses/PreviewDocument";
+import MakeExpense from "../../components/PaymentRequest/MakeExpense";
export const PaymentRequestContext = createContext();
export const usePaymentRequestContext = () => {
@@ -30,6 +31,8 @@ const PaymentRequestPage = () => {
IsOpen: false,
Image: null,
});
+ const [isExpenseGenerate,setIsExpenseGenerate] = useState({IsOpen: null,
+ RequestId: null,})
const [modalSize,setModalSize] = useState("md")
const [search, setSearch] = useState("");
@@ -37,7 +40,9 @@ const PaymentRequestPage = () => {
setManageRequest,
setVieRequest,
setDocumentView,
- setModalSize
+ setModalSize,
+ setIsExpenseGenerate,
+ isExpenseGenerate
};
useEffect(() => {
@@ -134,6 +139,15 @@ const PaymentRequestPage = () => {
)}
+ {isExpenseGenerate.IsOpen && (
+ setIsExpenseGenerate({IsOpen:false, requestId: null})}
+ >
+ setIsExpenseGenerate({IsOpen:false, requestId: null})} />
+
+ )}
{ViewDocument.IsOpen && (
api.post("/api/Expense/payment-request/action", data),
DeletePaymentRequest:()=>api.get("delete here come"),
+ CreatePaymentRequestExpense:(data)=>api.post('/api/Expense/payment-request/expense/create',data),
//#endregion
//#region Recurring Expense
| |