added currency,rename of ExpenseType to expenseCategory - hooks,( expenseCategoryIds = fileter object)

This commit is contained in:
pramod.mahajan 2025-11-08 09:10:35 +05:30
parent 6a97dcf5f6
commit 6f228ed5f1
8 changed files with 167 additions and 162 deletions

View File

@ -4,21 +4,19 @@ const ExpenseFilterChips = ({ filters, filterData, removeFilterChip }) => {
// Build chips from filters // Build chips from filters
const filterChips = useMemo(() => { const filterChips = useMemo(() => {
const chips = []; const chips = [];
const buildGroup = (ids, list, label, key) => { const buildGroup = (ids, list, label, key) => {
if (!ids?.length) return; if (!ids?.length) return;
const items = ids.map((id) => ({ const items = ids.map((id) => ({
id, id,
name: list.find((item) => item.id === id)?.name || id, name: list?.find((item) => item.id === id)?.name || id,
})); }));
chips.push({ key, label, items }); chips.push({ key, label, items });
}; };
buildGroup(filters.projectIds, filterData.projects, "Project", "projectIds"); buildGroup(filters.projectIds, filterData.projects, "Project", "projectIds");
buildGroup(filters.createdByIds, filterData.createdBy, "Submitted By", "createdByIds"); buildGroup(filters.createdByIds, filterData.createdBy, "Submitted By", "createdByIds");
buildGroup(filters.paidById, filterData.paidBy, "Paid By", "paidById"); buildGroup(filters.paidById, filterData.paidBy, "Paid By", "paidById");
buildGroup(filters.statusIds, filterData.status, "Status", "statusIds"); buildGroup(filters.statusIds, filterData.status, "Status", "statusIds");
buildGroup(filters.ExpenseTypeIds, filterData.expensesType, "Category", "ExpenseTypeIds"); buildGroup(filters.expenseCategoryIds, filterData.expenseCategory, "Category", "expenseCategoryIds");
if (filters.startDate || filters.endDate) { if (filters.startDate || filters.endDate) {
const start = filters.startDate const start = filters.startDate

View File

@ -31,7 +31,7 @@ const ExpenseFilterPanel = forwardRef(({ onApply, handleGroupBy, setFilterdata }
{ id: "submittedBy", name: "Submitted By" }, { id: "submittedBy", name: "Submitted By" },
{ id: "project", name: "Project" }, { id: "project", name: "Project" },
{ id: "paymentMode", name: "Payment Mode" }, { id: "paymentMode", name: "Payment Mode" },
{ id: "expensesType", name: "Expense Category" }, { id: "expenseCategory", name: "Expense Category" },
{ id: "createdAt", name: "Submitted Date" }, { id: "createdAt", name: "Submitted Date" },
].sort((a, b) => a.name.localeCompare(b.name)); ].sort((a, b) => a.name.localeCompare(b.name));
}, []); }, []);
@ -46,7 +46,7 @@ const ExpenseFilterPanel = forwardRef(({ onApply, handleGroupBy, setFilterdata }
projectIds: defaultFilter.projectIds || [], projectIds: defaultFilter.projectIds || [],
createdByIds: defaultFilter.createdByIds || [], createdByIds: defaultFilter.createdByIds || [],
paidById: defaultFilter.paidById || [], paidById: defaultFilter.paidById || [],
ExpenseCategoryIds: defaultFilter.ExpenseCategoryIds || [], expenseCategoryIds: defaultFilter.expenseCategoryIds || [],
isTransactionDate: defaultFilter.isTransactionDate ?? true, isTransactionDate: defaultFilter.isTransactionDate ?? true,
startDate: defaultFilter.startDate, startDate: defaultFilter.startDate,
endDate: defaultFilter.endDate, endDate: defaultFilter.endDate,
@ -214,7 +214,7 @@ const ExpenseFilterPanel = forwardRef(({ onApply, handleGroupBy, setFilterdata }
valueKey="id" valueKey="id"
/> />
<SelectMultiple <SelectMultiple
name="ExpenseCategoryIds" name="expenseCategoryIds"
label="Category :" label="Category :"
options={data.expenseCategory} options={data.expenseCategory}
labelKey={(item) => item.name} labelKey={(item) => item.name}

View File

@ -129,7 +129,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
align: "text-start mx-2", align: "text-start mx-2",
}, },
{ {
key: "expensesCategory", key: "expenseCategory",
label: "Expense Category", label: "Expense Category",
getValue: (e) => e.expenseCategory?.name || "N/A", getValue: (e) => e.expenseCategory?.name || "N/A",
align: "text-start", align: "text-start",
@ -225,6 +225,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
]?.includes(groupBy); ]?.includes(groupBy);
const canEditExpense = (expense) => { const canEditExpense = (expense) => {
debugger;
return ( return (
(expense?.status?.id === EXPENSE_DRAFT || (expense?.status?.id === EXPENSE_DRAFT ||
EXPENSE_REJECTEDBY.includes(expense?.status?.id)) && EXPENSE_REJECTEDBY.includes(expense?.status?.id)) &&
@ -352,8 +353,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
}) })
} }
></i> ></i>
{canDetetExpense(expense) && {canEditExpense(expense) && (
canEditExpense(expense) && (
<div className="dropdown z-2"> <div className="dropdown z-2">
<button <button
type="button" type="button"
@ -371,7 +371,6 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
></i> ></i>
</button> </button>
<ul className="dropdown-menu dropdown-menu-end w-auto"> <ul className="dropdown-menu dropdown-menu-end w-auto">
{canDetetExpense(expense) && (
<li <li
onClick={() => onClick={() =>
setManageExpenseModal({ setManageExpenseModal({
@ -387,7 +386,6 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
</span> </span>
</a> </a>
</li> </li>
)}
{canDetetExpense(expense) && ( {canDetetExpense(expense) && (
<li <li

View File

@ -10,7 +10,7 @@ const ALLOWED_TYPES = [
"image/jpeg", "image/jpeg",
]; ];
export const ExpenseSchema = (ExpenseCategories) => { export const ExpenseSchema = (expenseCategories) => {
return z return z
.object({ .object({
projectId: z.string().min(1, { message: "Project is required" }), projectId: z.string().min(1, { message: "Project is required" }),
@ -70,12 +70,12 @@ export const ExpenseSchema = (ExpenseCategories) => {
} }
) )
.superRefine((data, ctx) => { .superRefine((data, ctx) => {
const ExpenseCategory = ExpenseCategories.find( const ExpenseCategory = expenseCategories?.find(
(et) => et.id === data.expenseCategoryId (et) => et.id === data?.expenseCategoryId
); );
if ( if (
ExpenseCategory?.noOfPersonsRequired && ExpenseCategory?.noOfPersonsRequired &&
(!data.noOfPersons || data.noOfPersons < 1) (!data?.noOfPersons || data?.noOfPersons < 1)
) { ) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
@ -177,7 +177,7 @@ export const SearchSchema = z.object({
statusIds: z.array(z.string()).optional(), statusIds: z.array(z.string()).optional(),
createdByIds: z.array(z.string()).optional(), createdByIds: z.array(z.string()).optional(),
paidById: z.array(z.string()).optional(), paidById: z.array(z.string()).optional(),
ExpenseCategoryIds: z.array(z.string()).optional(), expenseCategoryIds: z.array(z.string()).optional(),
startDate: z.string().optional(), startDate: z.string().optional(),
endDate: z.string().optional(), endDate: z.string().optional(),
isTransactionDate: z.boolean().default(true), isTransactionDate: z.boolean().default(true),
@ -188,7 +188,7 @@ export const defaultFilter = {
statusIds: [], statusIds: [],
createdByIds: [], createdByIds: [],
paidById: [], paidById: [],
ExpenseCategoryIds: [], expenseCategoryIds: [],
isTransactionDate: true, isTransactionDate: true,
startDate: null, startDate: null,
endDate: null, endDate: null,

View File

@ -7,6 +7,7 @@ import { 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, {
useCurrencies,
useExpenseCategory, useExpenseCategory,
useExpenseStatus, useExpenseStatus,
usePaymentMode, usePaymentMode,
@ -29,6 +30,8 @@ import DatePicker from "../common/DatePicker";
import ErrorPage from "../../pages/ErrorPage"; 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 { DEFAULT_CURRENCY } from "../../utils/constants";
const ManageExpense = ({ closeModal, expenseToEdit = null }) => { const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
const { const {
@ -36,7 +39,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
isLoading, isLoading,
error: ExpenseErrorLoad, error: ExpenseErrorLoad,
} = useExpense(expenseToEdit); } = useExpense(expenseToEdit);
const [ExpenseType, setExpenseType] = useState(); const [expenseCategory, setExpenseCategory] = useState();
const dispatch = useDispatch(); const dispatch = useDispatch();
const { const {
expenseCategories, expenseCategories,
@ -65,7 +68,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,
@ -81,6 +88,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
isLoading: EmpLoading, isLoading: EmpLoading,
isError: isEmployeeError, isError: isEmployeeError,
} = useEmployeesNameByProject(selectedproject); } = useEmployeesNameByProject(selectedproject);
const files = watch("billAttachments"); const files = watch("billAttachments");
const onFileChange = async (e) => { const onFileChange = async (e) => {
const newFiles = Array.from(e.target.files); const newFiles = Array.from(e.target.files);
@ -157,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,
@ -195,7 +204,9 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
const expenseCategoryId = watch("expenseCategoryId"); const expenseCategoryId = watch("expenseCategoryId");
useEffect(() => { useEffect(() => {
setExpenseType(expenseCategories?.find((type) => type.id === expenseCategoryId)); setExpenseCategory(
expenseCategories?.find((type) => type.id === expenseCategoryId)
);
}, [expenseCategoryId]); }, [expenseCategoryId]);
const handleClose = () => { const handleClose = () => {
@ -238,7 +249,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
<div className="col-md-6"> <div className="col-md-6">
<Label htmlFor="expenseCategoryId" className="form-label" required> <Label htmlFor="expenseCategoryId" className="form-label" required>
Expense Type Expense Category
</Label> </Label>
<select <select
className="form-select form-select-sm" className="form-select form-select-sm"
@ -246,7 +257,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
{...register("expenseCategoryId")} {...register("expenseCategoryId")}
> >
<option value="" disabled> <option value="" disabled>
Select Type Select Category
</option> </option>
{ExpenseLoading ? ( {ExpenseLoading ? (
<option disabled>Loading...</option> <option disabled>Loading...</option>
@ -258,9 +269,9 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
)) ))
)} )}
</select> </select>
{errors.expenseCategoryId && ( {errors.expensesCategoryId && (
<small className="danger-text"> <small className="danger-text">
{errors.expenseCategoryId.message} {errors.expensesCategoryId.message}
</small> </small>
)} )}
</div> </div>
@ -295,34 +306,10 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
</small> </small>
)} )}
</div> </div>
<div className="col-12 col-md-6 text-start"> <div className="col-12 col-md-6 text-start">
<Label htmlFor="paidById" className="form-label" required> <Label className="form-label" required>
Paid By Paid By{" "}
</Label> </Label>
{/* <select
className="form-select form-select-sm"
id="paymentModeId"
{...register("paidById")}
disabled={!selectedproject}
>
<option value="" disabled>
Select Person
</option>
{EmpLoading ? (
<option disabled>Loading...</option>
) : (
employees?.map((emp) => (
<option key={emp.id} value={emp.id}>
{`${emp.firstName} ${emp.lastName} `}
</option>
))
)}
</select>
{errors.paidById && (
<small className="danger-text">{errors.paidById.message}</small>
)} */}
<EmployeeSearchInput <EmployeeSearchInput
control={control} control={control}
name="paidById" name="paidById"
@ -339,6 +326,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
</Label> </Label>
<DatePicker <DatePicker
name="transactionDate" name="transactionDate"
className="w-100"
control={control} control={control}
maxDate={new Date()} maxDate={new Date()}
/> />
@ -436,9 +424,37 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
)} )}
</div> </div>
{ExpenseType?.noOfPersonsRequired && ( </div>
<div className="col-md-6 mt-2 text-start"> <div className="row">
<label className="form-label ">No. of Persons</label> <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>
{expenseCategory?.noOfPersonsRequired && (
<div className="col-md-6 text-start">
<Label className="form-label" required>No. of Persons</Label>
<input <input
type="number" type="number"
id="noOfPersons" id="noOfPersons"
@ -510,40 +526,11 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
</small> </small>
)} )}
{files.length > 0 && ( {files.length > 0 && (
<div className="d-block"> <Filelist
{files files={files}
.filter((file) => { removeFile={removeFile}
if (expenseToEdit) { expenseToEdit={expenseToEdit}
return file.isActive; />
}
return true;
})
.map((file, idx) => (
<a
key={idx}
className="d-flex justify-content-between text-start p-1"
href={file.preSignedUrl || "#"}
target="_blank"
rel="noopener noreferrer"
>
<div>
<span className="mb-0 text-secondary small d-block">
{file.fileName}
</span>
<span className="text-body-secondary small d-block">
{file.fileSize ? formatFileSize(file.fileSize) : ""}
</span>
</div>
<i
className="bx bx-trash bx-sm cursor-pointer text-danger"
onClick={(e) => {
e.preventDefault();
removeFile(expenseToEdit ? file.documentId : idx);
}}
></i>
</a>
))}
</div>
)} )}
{Array.isArray(errors.billAttachments) && {Array.isArray(errors.billAttachments) &&

View File

@ -10,6 +10,16 @@ import {
} from "@tanstack/react-query"; } from "@tanstack/react-query";
import showToast from "../../services/toastService"; import showToast from "../../services/toastService";
export const useCurrencies = () => {
return useQuery({
queryKey: ["currencies"],
queryFn: async () => {
const resp = await MasterRespository.getCurrencies();
return resp.data;
},
});
};
export const usePaymentAjustmentHead = (isActive) => { export const usePaymentAjustmentHead = (isActive) => {
return useQuery({ return useQuery({
queryKey: ["paymentType",isActive], queryKey: ["paymentType",isActive],
@ -171,7 +181,7 @@ export const useExpenseCategory = () => {
}, },
}); });
return { ExpenseTypes, loading, error }; return { expenseCategories, loading, error };
}; };
export const usePaymentMode = () => { export const usePaymentMode = () => {
const { const {

View File

@ -190,7 +190,7 @@ const ExpensePage = () => {
{viewExpense.view && ( {viewExpense.view && (
<GlobalModel <GlobalModel
isOpen isOpen
size="lg" size="xl"
modalType="top" modalType="top"
closeModal={() => setViewExpense({ expenseId: null, view: false })} closeModal={() => setViewExpense({ expenseId: null, view: false })}
> >

View File

@ -50,7 +50,10 @@ export const MasterRespository = {
"Contact Category": (id) => api.delete(`/api/master/contact-category/${id}`), "Contact Category": (id) => api.delete(`/api/master/contact-category/${id}`),
"Contact Tag": (id) => api.delete(`/api/master/contact-tag/${id}`), "Contact Tag": (id) => api.delete(`/api/master/contact-tag/${id}`),
"Expense Type": (id, isActive) => "Expense Type": (id, isActive) =>
api.delete(`/api/Master/expenses-category/delete/${id}`, (isActive = false)), api.delete(
`/api/Master/expenses-category/delete/${id}`,
(isActive = false)
),
"Payment Mode": (id, isActive) => "Payment Mode": (id, isActive) =>
api.delete(`/api/Master/payment-mode/delete/${id}`, (isActive = false)), api.delete(`/api/Master/payment-mode/delete/${id}`, (isActive = false)),
"Expense Status": (id, isActive) => "Expense Status": (id, isActive) =>
@ -58,7 +61,11 @@ export const MasterRespository = {
"Document Type": (id) => api.delete(`/api/Master/document-type/delete/${id}`), "Document Type": (id) => api.delete(`/api/Master/document-type/delete/${id}`),
"Document Category": (id) => "Document Category": (id) =>
api.delete(`/api/Master/document-category/delete/${id}`), api.delete(`/api/Master/document-category/delete/${id}`),
"Payment Adjustment Head":(id,isActive)=>api.delete(`/api/Master/payment-adjustment-head/delete/${id}`,(isActive=false)), "Payment Adjustment Head": (id, isActive) =>
api.delete(
`/api/Master/payment-adjustment-head/delete/${id}`,
(isActive = false)
),
getWorkCategory: () => api.get(`/api/master/work-categories`), getWorkCategory: () => api.get(`/api/master/work-categories`),
createWorkCategory: (data) => api.post(`/api/master/work-category`, data), createWorkCategory: (data) => api.post(`/api/master/work-category`, data),
@ -79,7 +86,8 @@ export const MasterRespository = {
getAuditStatus: () => api.get("/api/Master/work-status"), getAuditStatus: () => api.get("/api/Master/work-status"),
getExpenseCategories: () => api.get("/api/Master/expenses-categories"), getExpenseCategories: () => api.get("/api/Master/expenses-categories"),
createExpenseCategory: (data) => api.post("/api/Master/expenses-category", data), createExpenseCategory: (data) =>
api.post("/api/Master/expenses-category", data),
updateExpenseCategory: (id, data) => updateExpenseCategory: (id, data) =>
api.put(`/api/Master/expenses-category/edit/${id}`, data), api.put(`/api/Master/expenses-category/edit/${id}`, data),
@ -134,6 +142,10 @@ export const MasterRespository = {
getPaymentAdjustmentHead: (isActive) => getPaymentAdjustmentHead: (isActive) =>
api.get(`/api/Master/payment-adjustment-head/list?isActive=${isActive}`), api.get(`/api/Master/payment-adjustment-head/list?isActive=${isActive}`),
createPaymentAjustmentHead:(data)=>api.post(`/api/Master/payment-adjustment-head`, data), createPaymentAjustmentHead: (data) =>
updatePaymentAjustmentHead:(id,data)=>api.put(`/api/Master/payment-adjustment-head/edit/${id}`, data) api.post(`/api/Master/payment-adjustment-head`, data),
updatePaymentAjustmentHead: (id, data) =>
api.put(`/api/Master/payment-adjustment-head/edit/${id}`, data),
getCurrencies: () => api.get(`/api/Master/currencies/list`),
}; };