added new fields inside Expense action - tds, baseAmount, taxAmount

This commit is contained in:
pramod.mahajan 2025-11-05 17:04:04 +05:30
parent daaebf919d
commit ec2a1706fc
8 changed files with 488 additions and 322 deletions

View File

@ -124,9 +124,9 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
align: "text-start mx-2", align: "text-start mx-2",
}, },
{ {
key: "expensesType", key: "expensesCategory",
label: "Expense Category", label: "Expense Category",
getValue: (e) => e.expensesType?.name || "N/A", getValue: (e) => e.expenseCategory?.name || "N/A",
align: "text-start", align: "text-start",
}, },
{ {
@ -254,7 +254,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
className={`sorting d-table-cell `} className={`sorting d-table-cell `}
aria-sort="descending" aria-sort="descending"
> >
<div className={`${col.align} p`}>{col.label}</div> <div className={`${col.align} `}>{col.label}</div>
</th> </th>
) )
)} )}
@ -289,9 +289,12 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
(col.isAlwaysVisible || groupBy !== col.key) && ( (col.isAlwaysVisible || groupBy !== col.key) && (
<td <td
key={col.key} key={col.key}
className={`d-table-cell ml-2 ${col.align ?? ""}`} className={`d-table-cell ml-2 ${col.align ?? ""} `}
> >
<div className="d-flex px-2">{col.customRender <div className={`d-flex px-2 ${col.key === "status" ? "justify-content-center":""}
${col.key === "amount" ? "justify-content-end":""}
${col.key === "submitted" ? "justify-content-center":""}
`}>{col.customRender
? col.customRender(expense) ? col.customRender(expense)
: col.getValue(expense)}</div> : col.getValue(expense)}</div>
</td> </td>

View File

@ -1,5 +1,6 @@
import { z } from "zod"; import { z } from "zod";
import { localToUtc } from "../../utils/appUtils"; import { localToUtc } from "../../utils/appUtils";
import { DEFAULT_CURRENCY } from "../../utils/constants";
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const ALLOWED_TYPES = [ const ALLOWED_TYPES = [
@ -24,6 +25,10 @@ export const ExpenseSchema = (expenseTypes) => {
location: z.string().min(1, { message: "Location is required" }), location: z.string().min(1, { message: "Location is required" }),
supplerName: z.string().min(1, { message: "Supplier name is required" }), supplerName: z.string().min(1, { message: "Supplier name is required" }),
gstNumber: z.string().optional(), gstNumber: z.string().optional(),
currencyId: z
.string()
.min(1, { message: "currency is required" })
.default(DEFAULT_CURRENCY),
amount: z.coerce amount: z.coerce
.number({ .number({
invalid_type_error: "Amount is required and must be a number", invalid_type_error: "Amount is required and must be a number",
@ -94,6 +99,7 @@ export const defaultExpense = {
amount: "", amount: "",
noOfPersons: "", noOfPersons: "",
gstNumber: "", gstNumber: "",
currencyId: DEFAULT_CURRENCY,
billAttachments: [], billAttachments: [],
}; };
@ -108,6 +114,9 @@ export const ExpenseActionScheam = (
reimburseTransactionId: z.string().nullable().optional(), reimburseTransactionId: z.string().nullable().optional(),
reimburseDate: z.string().nullable().optional(), reimburseDate: z.string().nullable().optional(),
reimburseById: z.string().nullable().optional(), reimburseById: z.string().nullable().optional(),
tdsPercentage: z.number().optional(),
baseAmount: z.string().nullable().optional(),
taxAmount: z.string().nullable().optional(),
}) })
.superRefine((data, ctx) => { .superRefine((data, ctx) => {
if (isReimbursement) { if (isReimbursement) {
@ -125,15 +134,7 @@ export const ExpenseActionScheam = (
message: "Reimburse Date is required", message: "Reimburse Date is required",
}); });
} }
// let reimburse_Date = localToUtc(data.reimburseDate);
// if (transactionDate > reimburse_Date) {
// ctx.addIssue({
// code: z.ZodIssueCode.custom,
// path: ["reimburseDate"],
// message:
// "Reimburse Date must be greater than or equal to Expense created Date",
// });
// }
if (!data.reimburseById) { if (!data.reimburseById) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
@ -141,6 +142,20 @@ export const ExpenseActionScheam = (
message: "Reimburse By is required", message: "Reimburse By is required",
}); });
} }
if (!data.baseAmount) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["baseAmount"],
message: "Base Amount i required",
});
}
if (!data.taxAmount) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["taxAmount"],
message: "Tax is required",
});
}
} }
}); });
}; };
@ -151,7 +166,10 @@ export const defaultActionValues = {
reimburseTransactionId: null, reimburseTransactionId: null,
reimburseDate: null, reimburseDate: null,
reimburseById: null, reimburseById: "",
tdsPercentage: 0,
baseAmount: "",
taxAmount: "",
}; };
export const SearchSchema = z.object({ export const SearchSchema = z.object({

View File

@ -3,7 +3,7 @@ import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { defaultExpense, ExpenseSchema } from "./ExpenseSchema"; import { defaultExpense, ExpenseSchema } from "./ExpenseSchema";
import { formatFileSize, localToUtc } from "../../utils/appUtils"; import { formatFileSize, localToUtc } from "../../utils/appUtils";
import { useProjectName } from "../../hooks/useProjects"; import { useCurrencies, useProjectName } from "../../hooks/useProjects";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { changeMaster } from "../../slices/localVariablesSlice"; import { changeMaster } from "../../slices/localVariablesSlice";
import useMaster, { import useMaster, {
@ -30,7 +30,7 @@ import ErrorPage from "../../pages/ErrorPage";
import Label from "../common/Label"; import Label from "../common/Label";
import EmployeeSearchInput from "../common/EmployeeSearchInput"; import EmployeeSearchInput from "../common/EmployeeSearchInput";
import Filelist from "./Filelist"; import Filelist from "./Filelist";
import { DEFAULT_CURRENCY } from "../../utils/constants";
const ManageExpense = ({ closeModal, expenseToEdit = null }) => { const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
const { const {
@ -67,7 +67,11 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
error, error,
isError: isProjectError, isError: isProjectError,
} = useProjectName(); } = useProjectName();
const {
data: currencies,
isLoading: currencyLoading,
error: currencyError,
} = useCurrencies();
const { const {
PaymentModes, PaymentModes,
loading: PaymentModeLoading, loading: PaymentModeLoading,
@ -129,7 +133,8 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
reader.onload = () => resolve(reader.result.split(",")[1]); reader.onload = () => resolve(reader.result.split(",")[1]);
reader.onerror = (error) => reject(error); reader.onerror = (error) => reject(error);
}); });
const removeFile = (index) => {documentId const removeFile = (index) => {
documentId;
if (expenseToEdit) { if (expenseToEdit) {
const newFiles = files.map((file, i) => { const newFiles = files.map((file, i) => {
if (file.documentId !== index) return file; if (file.documentId !== index) return file;
@ -160,6 +165,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
amount: data.amount || "", amount: data.amount || "",
noOfPersons: data.noOfPersons || "", noOfPersons: data.noOfPersons || "",
gstNumber: data.gstNumber || "", gstNumber: data.gstNumber || "",
currencyId: data.currencyId || DEFAULT_CURRENCY,
billAttachments: data.documents billAttachments: data.documents
? data.documents.map((doc) => ({ ? data.documents.map((doc) => ({
fileName: doc.fileName, fileName: doc.fileName,
@ -198,7 +204,9 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
const ExpenseTypeId = watch("expensesCategoryId"); const ExpenseTypeId = watch("expensesCategoryId");
useEffect(() => { useEffect(() => {
setExpenseType(ExpenseCategories?.find((type) => type.id === ExpenseTypeId)); setExpenseType(
ExpenseCategories?.find((type) => type.id === ExpenseTypeId)
);
}, [ExpenseTypeId]); }, [ExpenseTypeId]);
const handleClose = () => { const handleClose = () => {
@ -299,7 +307,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
)} )}
</div> </div>
<div className="col-12 col-md-6 text-start"> <div className="col-12 col-md-6 text-start">
<label className="form-label">Paid By </label> <Label className="form-label" required>Paid By </Label>
<EmployeeSearchInput <EmployeeSearchInput
control={control} control={control}
name="paidById" name="paidById"
@ -432,6 +440,32 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
</div> </div>
)} )}
</div> </div>
<div className="col-md-6 text-start">
<Label htmlFor="currencyId" className="form-label" required>
Select Currency
</Label>
<select
className="form-select form-select-sm"
id="currencyId"
{...register("currencyId")}
>
<option value="" disabled>
Select Currency
</option>
{currencyLoading ? (
<option disabled>Loading...</option>
) : (
currencies?.map((currency) => (
<option key={currency.id} value={currency.id}>
{`${currency.currencyName} (${currency.symbol}) `}
</option>
))
)}
</select>
{errors.currencyId && (
<small className="danger-text">{errors.currencyId.message}</small>
)}
</div>
<div className="row my-2 text-start"> <div className="row my-2 text-start">
<div className="col-md-12"> <div className="col-md-12">
@ -452,7 +486,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
</div> </div>
</div> </div>
<div className="row my-2 text-start"> <div className="row my-2 text-start">
<div className="col-md-12"> <div className="col-md-12">
<Label className="form-label" required> <Label className="form-label" required>
Upload Bill{" "} Upload Bill{" "}
@ -487,7 +521,13 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
{errors.billAttachments.message} {errors.billAttachments.message}
</small> </small>
)} )}
{files.length > 0 && <Filelist files={files} removeFile={removeFile} expenseToEdit={expenseToEdit}/>} {files.length > 0 && (
<Filelist
files={files}
removeFile={removeFile}
expenseToEdit={expenseToEdit}
/>
)}
{Array.isArray(errors.billAttachments) && {Array.isArray(errors.billAttachments) &&
errors.billAttachments.map((fileError, index) => ( errors.billAttachments.map((fileError, index) => (

View File

@ -110,326 +110,430 @@ const ViewExpense = ({ ExpenseId }) => {
const handleImageLoad = (id) => { const handleImageLoad = (id) => {
setImageLoaded((prev) => ({ ...prev, [id]: true })); setImageLoaded((prev) => ({ ...prev, [id]: true }));
}; };
return ( return (
<form className="container px-3" onSubmit={handleSubmit(onSubmit)}> <form className="container px-3" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 mb-1"> <div className="col-12 mb-1">
<h5 className="fw-semibold m-0">Expense Details</h5> <h5 className="fw-semibold m-0">Expense Details</h5>
</div> </div>
<div className="row mb-1 "> <div className="row mb-1 ">
<div className="col-12 col-lg-7 col-xl-8 mb-3"> <div className="col-12 col-lg-7 col-xl-8 mb-3">
<div className="row"> <div className="row">
<div className="col-12 d-flex justify-content-between text-start fw-semibold my-2"> <div className="col-12 d-flex justify-content-between text-start fw-semibold my-2">
<span>{data?.expenseUId}</span> <span>{data?.expenseUId}</span>
<span <span
className={`badge bg-label-${ className={`badge bg-label-${
getColorNameFromHex(data?.status?.color) || "secondary" getColorNameFromHex(data?.status?.color) || "secondary"
}`}t }`}
> t
{data?.status?.name} >
</span> {data?.status?.name}
</div>
{/* Row 1 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold text-start" style={{ minWidth: "130px" }}>
Transaction Date :
</label>
<div className="text-muted">
{formatUTCToLocalTime(data?.transactionDate)}
</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold text-start" style={{ minWidth: "130px" }}>
Expense Type :
</label>
<div className="text-muted">{data?.expensesType?.name}</div>
</div>
</div>
{/* Row 2 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold text-start" style={{ minWidth: "130px" }}>
Supplier :
</label>
<div className="text-muted">{data?.supplerName}</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold text-start" style={{ minWidth: "130px" }}>
Amount :
</label>
<div className="text-muted"> {formatFigure(data?.amount,{type:"currency",currency : data?.currency?.currencyCode ?? "INR"} )}</div>
</div>
</div>
{/* Row 3 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold text-start" style={{ minWidth: "130px" }}>
Payment Mode :
</label>
<div className="text-muted">{data?.paymentMode?.name}</div>
</div>
</div>
{data?.gstNumber && (
<div className="col-md-6 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold text-start" style={{ minWidth: "130px" }}>
GST Number :
</label>
<div className="text-muted">{data?.gstNumber}</div>
</div>
</div>
)}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold text-start" style={{ minWidth: "130px" }}>
Pre-Approved :
</label>
<div className="text-muted">{data.preApproved ? "Yes" : "No"}</div>
</div>
</div>
{/* Row 5 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold text-start" style={{ minWidth: "130px" }}>
Project :
</label>
<div className="text-muted">{data?.project?.name}</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold text-start" style={{ minWidth: "130px" }}>
Created At :
</label>
<div className="text-muted">{formatUTCToLocalTime(data?.createdAt, true)}</div>
</div>
</div>
{/* Created & Paid By */}
{data.createdBy && (
<div className="col-md-6 text-start mb-3">
<div className="d-flex align-items-center">
<label className="form-label me-2 mb-0 fw-semibold" style={{ minWidth: "130px" }}>
Created By :
</label>
<Avatar
size="xs"
classAvatar="m-0 me-1"
firstName={data.createdBy?.firstName}
lastName={data.createdBy?.lastName}
/>
<span className="text-muted">
{`${data.createdBy?.firstName ?? ""} ${data.createdBy?.lastName ?? ""}`.trim() || "N/A"}
</span> </span>
</div> </div>
</div>
)}
<div className="col-md-6 text-start mb-3"> {/* Row 1 */}
<div className="d-flex align-items-center"> <div className="col-md-6 mb-3">
<label className="form-label me-2 mb-0 fw-semibold" style={{ minWidth: "130px" }}> <div className="d-flex">
Paid By : <label
</label> className="form-label me-2 mb-0 fw-semibold text-start"
<Avatar style={{ minWidth: "130px" }}
size="xs"
classAvatar="m-0 me-1"
firstName={data.paidBy?.firstName}
lastName={data.paidBy?.lastName}
/>
<span className="text-muted">
{`${data.paidBy?.firstName ?? ""} ${data.paidBy?.lastName ?? ""}`.trim() || "N/A"}
</span>
</div>
</div>
{/* Description */}
<div className="col-12 text-start mb-2">
<label className="fw-semibold form-label">Description : </label>
<div className="text-muted">{data?.description}</div>
</div>
{/* Attachments */}
<div className="col-12 text-start mb-2">
<label className="form-label me-2 mb-2 fw-semibold">Attachment :</label>
<div className="d-flex flex-wrap gap-2">
{data?.documents?.map((doc) => {
const isImage = doc.contentType?.includes("image");
return (
<div
key={doc.documentId}
className="d-flex align-items-center cusor-pointer"
onClick={() => {
if (isImage) {
setDocumentView({
IsOpen: true,
Image: doc.preSignedUrl,
});
}
}}
> >
<i className={`bx ${getIconByFileType(doc.contentType)}`} style={{ fontSize: "30px" }}></i> Transaction Date :
<small className="text-center text-tiny text-truncate w-100" title={doc.fileName}> </label>
{doc.fileName} <div className="text-muted">
</small> {formatUTCToLocalTime(data?.transactionDate)}
</div> </div>
); </div>
})} </div>
</div> <div className="col-md-6 mb-3">
</div> <div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Expense Type :
</label>
<div className="text-muted">{data?.expensesType?.name}</div>
</div>
</div>
{data.expensesReimburse && ( {/* Row 2 */}
<div className="row text-start mt-2"> <div className="col-md-6 mb-3">
<div className="col-md-6 mb-sm-0 mb-2"> <div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold"> <label
Transaction ID : className="form-label me-2 mb-0 fw-semibold text-start"
</label> style={{ minWidth: "130px" }}
{data.expensesReimburse.reimburseTransactionId || "N/A"} >
</div> Supplier :
<div className="col-md-6 "> </label>
<label className="form-label me-2 mb-0 fw-semibold"> <div className="text-muted">{data?.supplerName}</div>
Reimburse Date : </div>
</label> </div>
{formatUTCToLocalTime(data.expensesReimburse.reimburseDate)}
</div>
{data.expensesReimburse && ( <div className="col-md-6 mb-3">
<> <div className="d-flex">
<div className="col-md-6 d-flex align-items-center"> <label
<label className="form-label me-2 mb-0 fw-semibold"> className="form-label me-2 mb-0 fw-semibold text-start"
Reimburse By : style={{ minWidth: "130px" }}
>
Amount :
</label>
<div className="text-muted">
{" "}
{formatFigure(data?.amount, {
type: "currency",
currency: data?.currency?.currencyCode ?? "INR",
})}
</div>
</div>
</div>
{/* Row 3 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Payment Mode :
</label>
<div className="text-muted">{data?.paymentMode?.name}</div>
</div>
</div>
{data?.gstNumber && (
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
GST Number :
</label>
<div className="text-muted">{data?.gstNumber}</div>
</div>
</div>
)}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Pre-Approved :
</label>
<div className="text-muted">
{data.preApproved ? "Yes" : "No"}
</div>
</div>
</div>
{/* Row 5 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Project :
</label>
<div className="text-muted">{data?.project?.name}</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Created At :
</label>
<div className="text-muted">
{formatUTCToLocalTime(data?.createdAt, true)}
</div>
</div>
</div>
{/* Created & Paid By */}
{data.createdBy && (
<div className="col-md-6 text-start mb-3">
<div className="d-flex align-items-center">
<label
className="form-label me-2 mb-0 fw-semibold"
style={{ minWidth: "130px" }}
>
Created By :
</label>
<Avatar
size="xs"
classAvatar="m-0 me-1"
firstName={data.createdBy?.firstName}
lastName={data.createdBy?.lastName}
/>
<span className="text-muted">
{`${data.createdBy?.firstName ?? ""} ${
data.createdBy?.lastName ?? ""
}`.trim() || "N/A"}
</span>
</div>
</div>
)}
<div className="col-md-6 text-start mb-3">
<div className="d-flex align-items-center">
<label
className="form-label me-2 mb-0 fw-semibold"
style={{ minWidth: "130px" }}
>
Paid By :
</label> </label>
<Avatar <Avatar
size="xs" size="xs"
classAvatar="m-0 me-1" classAvatar="m-0 me-1"
firstName={data?.expensesReimburse?.reimburseBy?.firstName} firstName={data.paidBy?.firstName}
lastName={data?.expensesReimburse?.reimburseBy?.lastName} lastName={data.paidBy?.lastName}
/> />
<span className="text-muted"> <span className="text-muted">
{`${data?.expensesReimburse?.reimburseBy?.firstName} ${data?.expensesReimburse?.reimburseBy?.lastName}`.trim()} {`${data.paidBy?.firstName ?? ""} ${
data.paidBy?.lastName ?? ""
}`.trim() || "N/A"}
</span> </span>
</div> </div>
</> </div>
)}
</div>
)}
<hr className="divider my-1 border-2 divider-primary my-2" />
{Array.isArray(data?.nextStatus) && data.nextStatus.length > 0 && ( {/* Description */}
<> <div className="col-12 text-start mb-2">
{IsPaymentProcess && nextStatusWithPermission?.length > 0 && ( <label className="fw-semibold form-label">Description : </label>
<div className="row"> <div className="text-muted">{data?.description}</div>
<div className="col-12 col-md-6 text-start"> </div>
<label className="form-label">Transaction Id </label>
<input {/* Attachments */}
type="text" <div className="col-12 text-start mb-2">
className="form-control form-control-sm" <label className="form-label me-2 mb-2 fw-semibold">
{...register("reimburseTransactionId")} Attachment :
/> </label>
{errors.reimburseTransactionId && ( <div className="d-flex flex-wrap gap-2">
<small className="danger-text"> {data?.documents?.map((doc) => {
{errors.reimburseTransactionId.message} const isImage = doc.contentType?.includes("image");
</small> return (
)} <div
</div> key={doc.documentId}
<div className="col-12 col-md-6 text-start"> className="d-flex align-items-center cusor-pointer"
<label className="form-label">Transaction Date </label> onClick={() => {
<DatePicker if (isImage) {
name="reimburseDate" setDocumentView({
control={control} IsOpen: true,
minDate={data?.transactionDate} Image: doc.preSignedUrl,
maxDate={new Date()} });
/> }
{errors.reimburseDate && ( }}
<small className="danger-text"> >
{errors.reimburseDate.message} <i
</small> className={`bx ${getIconByFileType(doc.contentType)}`}
)} style={{ fontSize: "30px" }}
</div> ></i>
<div className="col-12 col-md-6 text-start"> <small
<label className="form-label">Reimburse By </label> className="text-center text-tiny text-truncate w-100"
<EmployeeSearchInput title={doc.fileName}
control={control} >
name="reimburseById" {doc.fileName}
projectId={null} </small>
/> </div>
);
})}
</div> </div>
</div> </div>
)}
<div className="col-12 mb-3 text-start"> {data.expensesReimburse && (
{((nextStatusWithPermission.length > 0 && !IsRejectedExpense) || <div className="row text-start mt-2">
(IsRejectedExpense && isCreatedBy)) && ( <div className="col-md-6 mb-sm-0 mb-2">
<> <label className="form-label me-2 mb-0 fw-semibold">
<Label className="form-label me-2 mb-0" required> Transaction ID :
Comment </label>
</Label> {data.expensesReimburse.reimburseTransactionId || "N/A"}
<textarea </div>
className="form-control form-control-sm" <div className="col-md-6 ">
{...register("comment")} <label className="form-label me-2 mb-0 fw-semibold">
rows="2" Reimburse Date :
/> </label>
{errors.comment && ( {formatUTCToLocalTime(data.expensesReimburse.reimburseDate)}
<small className="danger-text"> </div>
{errors.comment.message}
</small> {data.expensesReimburse && (
<>
<div className="col-md-6 d-flex align-items-center">
<label className="form-label me-2 mb-0 fw-semibold">
Reimburse By :
</label>
<Avatar
size="xs"
classAvatar="m-0 me-1"
firstName={
data?.expensesReimburse?.reimburseBy?.firstName
}
lastName={
data?.expensesReimburse?.reimburseBy?.lastName
}
/>
<span className="text-muted">
{`${data?.expensesReimburse?.reimburseBy?.firstName} ${data?.expensesReimburse?.reimburseBy?.lastName}`.trim()}
</span>
</div>
</>
)} )}
</div>
)}
<hr className="divider my-1 border-2 divider-primary my-2" />
{Array.isArray(data?.nextStatus) && data.nextStatus.length > 0 && (
<>
{IsPaymentProcess && nextStatusWithPermission?.length > 0 && (
<div className="row ">
<div className="col-12 col-md-6 text-start">
<label className="form-label">Transaction Id </label>
<input
type="text"
className="form-control form-control-sm"
{...register("reimburseTransactionId")}
/>
{errors.reimburseTransactionId && (
<small className="danger-text">
{errors.reimburseTransactionId.message}
</small>
)}
</div>
<div className="col-12 col-md-6 text-start mb-2">
<label className="form-label">Transaction Date </label>
<DatePicker
name="reimburseDate"
control={control}
minDate={data?.transactionDate}
maxDate={new Date()}
/>
{errors.reimburseDate && (
<small className="danger-text">
{errors.reimburseDate.message}
</small>
)}
</div>
<div className="col-12 col-md-6 text-start mb-2">
<Label className="form-label" required>
Reimburse By{" "}
</Label>
<EmployeeSearchInput
control={control}
name="reimburseById"
projectId={null}
/>
</div>
<div className="col-12 col-md-6 text-start">
<Label className="form-label" >
TDS Percentage
</Label>
<input
type="number"
className="form-control form-control-sm"
{...register("tdsPercentage")}
/>
{errors.tdsPercentage && (
<small className="danger-text">
{errors.tdsPercentage.message}
</small>
)}
</div>
<div className="col-12 col-md-6 text-start">
<Label className="form-label" required>
Base Amount
</Label>
<input
type="number"
className="form-control form-control-sm"
{...register("baseAmount")}
/>
{errors.baseAmount && (
<small className="danger-text">
{errors.baseAmount.message}
</small>
)}
</div>
<div className="col-12 col-md-6 text-start">
<Label className="form-label" required>
Tax Amount
</Label>
<input
type="number"
className="form-control form-control-sm"
{...register("taxAmount")}
/>
{errors.taxAmount && (
<small className="danger-text">
{errors.taxAmount.message}
</small>
)}
</div>
</div>
)}
<div className="col-12 mb-3 text-start mt-2">
{((nextStatusWithPermission.length > 0 &&
!IsRejectedExpense) ||
(IsRejectedExpense && isCreatedBy)) && (
<>
<Label className="form-label me-2 mb-0" required>
Comment
</Label>
<textarea
className="form-control form-control-sm"
{...register("comment")}
rows="2"
/>
{errors.comment && (
<small className="danger-text">
{errors.comment.message}
</small>
)}
</>
)}
{nextStatusWithPermission?.length > 0 &&
(!IsRejectedExpense || isCreatedBy) && (
<div className="text-end flex-wrap gap-2 my-2 mt-3">
{nextStatusWithPermission.map((status, index) => (
<button
key={status.id || index}
type="button"
onClick={() => {
setClickedStatusId(status.id);
setValue("statusId", status.id);
handleSubmit(onSubmit)();
}}
disabled={isPending || isFetching}
className="btn btn-primary btn-sm cursor-pointer mx-2 border-0"
>
{isPending && clickedStatusId === status.id
? "Please Wait..."
: status.displayName || status.name}
</button>
))}
</div>
)}
</div>
</> </>
)} )}
{nextStatusWithPermission?.length > 0 &&
(!IsRejectedExpense || isCreatedBy) && (
<div className="text-end flex-wrap gap-2 my-2 mt-3">
{nextStatusWithPermission.map((status, index) => (
<button
key={status.id || index}
type="button"
onClick={() => {
setClickedStatusId(status.id);
setValue("statusId", status.id);
handleSubmit(onSubmit)();
}}
disabled={isPending || isFetching}
className="btn btn-primary btn-sm cursor-pointer mx-2 border-0"
>
{isPending && clickedStatusId === status.id
? "Please Wait..."
: status.displayName || status.name}
</button>
))}
</div>
)}
</div> </div>
</> </div>
)}
</div>
</div>
<div className="col-12 col-lg-5 col-xl-4"> <div className="col-12 col-lg-5 col-xl-4">
<div className="d-flex align-items-center text-secondary mb-"> <div className="d-flex align-items-center text-secondary mb-">
<i className='bx bx-time-five me-2'></i> <p className=" m-0">TimeLine</p> <i className="bx bx-time-five me-2"></i>{" "}
<p className=" m-0">TimeLine</p>
</div>
<ExpenseStatusLogs data={data} />
</div>
</div> </div>
<ExpenseStatusLogs data={data} /> </form>
</div>
</div>
</form>
); );
}; };

View File

@ -370,7 +370,7 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
onChange={handleSetItSelf} onChange={handleSetItSelf}
/> />
<Label htmlFor="sameAsSupplier" className="form-check-label"> <Label htmlFor="sameAsSupplier" className="form-check-label">
Same as Supplier It self
</Label> </Label>
</div> </div>
</div> </div>
@ -425,7 +425,7 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
{/* Upload Document */} {/* Upload Document */}
<div className="row my-2 text-start"> <div className="row my-2 text-start">
<div className="col-md-12"> <div className="col-md-12">
<Label className="form-label" required> <Label className="form-label">
Upload Bill{" "} Upload Bill{" "}
</Label> </Label>

View File

@ -441,9 +441,9 @@ const ViewPaymentRequest = ({ requestId }) => {
)} )}
</div> </div>
</> </>
):(<div className="text-end flex-wrap gap-2 my-2 mt-3"> ): !data?.isExpenseCreated ? (<div className="text-end flex-wrap gap-2 my-2 mt-3">
<button className="btn btn-sm btn-primary" onClick={handleExpense}>Make Expense</button> <button className="btn btn-sm btn-primary" onClick={handleExpense}>Make Expense</button>
</div>)} </div>):(<></>)}
</div> </div>
</div> </div>

View File

@ -115,7 +115,7 @@ const EmployeeSearchInput = ({
</ul> </ul>
)} )}
{error && <small className="text-danger">{error.message}</small>} {error && <small className="danger-text">{error.message}</small>}
</div> </div>
); );
}; };

View File

@ -157,6 +157,7 @@ export const PROJECT_STATUS = [
}, },
]; ];
export const DEFAULT_CURRENCY = "78e96e4a-7ce0-4164-ae3a-c833ad45ec2c";
export const EXPENSE_STATUS = { export const EXPENSE_STATUS = {
daft:"297e0d8f-f668-41b5-bfea-e03b354251c8", daft:"297e0d8f-f668-41b5-bfea-e03b354251c8",