Adding Create API for Payment Request.

This commit is contained in:
Kartik Sharma 2025-11-01 17:10:47 +05:30
parent 196069b39a
commit c123373892
5 changed files with 132 additions and 96 deletions

View File

@ -1,5 +1,5 @@
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import { useProjectName } from '../../hooks/useProjects'; import { useCurrencies, useProjectName } from '../../hooks/useProjects';
import Label from '../common/Label'; import Label from '../common/Label';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useExpenseType } from '../../hooks/masterHook/useMaster'; import { useExpenseType } from '../../hooks/masterHook/useMaster';
@ -11,8 +11,14 @@ import { formatFileSize, localToUtc } from '../../utils/appUtils';
function ManagePaymentRequest({ closeModal, expenseToEdit = null }) { function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
const { data } = {}
const { projectNames, loading: projectLoading, error, isError: isProjectError, const { projectNames, loading: projectLoading, error, isError: isProjectError,
} = useProjectName(); } = useProjectName();
const { data: currencyData, isLoading: currencyLoading, isError: currencyError } = useCurrencies();
const { const {
ExpenseTypes, ExpenseTypes,
loading: ExpenseLoading, loading: ExpenseLoading,
@ -98,39 +104,39 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
} }
); );
// useEffect(() => { useEffect(() => {
// if (expenseToEdit && data) { if (expenseToEdit && data) {
// reset({ reset({
// title: data.title || "", title: data.title || "",
// description: data.description || "", description: data.description || "",
// payee: data.payee || "", payee: data.payee || "",
// currencyId: data.currencyId.id || "", currencyId: data.currencyId.id || "",
// amount: data.amount || "", amount: data.amount || "",
// dueDate: data.dueDate?.slice(0, 10) || "", dueDate: data.dueDate?.slice(0, 10) || "",
// projectId: data.project.id || "", projectId: data.project.id || "",
// expenseCategoryId: data.expenseCategoryId.id || "", expenseCategoryId: data.expenseCategoryId.id || "",
// isAdvancePayment: data.isAdvancePayment || "", isAdvancePayment: data.isAdvancePayment || false,
// billAttachments: data.documents billAttachments: data.documents
// ? data.documents.map((doc) => ({ ? data.documents.map((doc) => ({
// fileName: doc.fileName, fileName: doc.fileName,
// base64Data: null, base64Data: null,
// contentType: doc.contentType, contentType: doc.contentType,
// documentId: doc.documentId, documentId: doc.documentId,
// fileSize: 0, fileSize: 0,
// description: "", description: "",
// preSignedUrl: doc.preSignedUrl, preSignedUrl: doc.preSignedUrl,
// isActive: doc.isActive ?? true, isActive: doc.isActive ?? true,
// })) }))
// : [], : [],
// }); });
// } }
// }, [data, reset, employees]); }, [data, reset]);
const onSubmit = (fromdata) => { const onSubmit = (fromdata) => {
let payload = { let payload = {
...fromdata, ...fromdata,
transactionDate: localToUtc(fromdata.transactionDate), dueDate: localToUtc(fromdata.dueDate),
}; };
if (expenseToEdit) { if (expenseToEdit) {
const editPayload = { ...payload, id: data.id }; const editPayload = { ...payload, id: data.id };
@ -140,6 +146,7 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
} }
}; };
return ( return (
<div className="container p-3"> <div className="container p-3">
<h5 className="m-0"> <h5 className="m-0">
@ -172,13 +179,13 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
</div> </div>
<div className="col-md-6"> <div className="col-md-6">
<Label htmlFor="expensesTypeId" className="form-label" required> <Label htmlFor="expenseCategoryId" className="form-label" required>
Expense Category Expense Category
</Label> </Label>
<select <select
className="form-select form-select-sm" className="form-select form-select-sm"
id="expensesTypeId" id="expenseCategoryId"
{...register("expensesTypeId")} {...register("expenseCategoryId")}
> >
<option value="" disabled> <option value="" disabled>
Select Type Select Type
@ -193,9 +200,9 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
)) ))
)} )}
</select> </select>
{errors.expensesTypeId && ( {errors.expenseCategoryId && (
<small className="danger-text"> <small className="danger-text">
{errors.expensesTypeId.message} {errors.expenseCategoryId.message}
</small> </small>
)} )}
</div> </div>
@ -204,18 +211,18 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
<div className="row my-2 text-start"> <div className="row my-2 text-start">
<div className="col-md-6"> <div className="col-md-6">
<Label htmlFor="transactionDate" className="form-label" required> <Label htmlFor="dueDate" className="form-label" required>
Transaction Date Transaction Date
</Label> </Label>
<DatePicker <DatePicker
name="transactionDate" name="dueDate"
control={control} control={control}
maxDate={new Date()} maxDate={new Date()}
/> />
{errors.transactionDate && ( {errors.dueDate && (
<small className="danger-text"> <small className="danger-text">
{errors.transactionDate.message} {errors.dueDate.message}
</small> </small>
)} )}
</div> </div>
@ -241,36 +248,48 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
<div className="row my-2 text-start"> <div className="row my-2 text-start">
<div className="col-md-6"> <div className="col-md-6">
<Label htmlFor="supplerName" className="form-label" required> <Label htmlFor="payee" className="form-label" required>
Supplier Name/Transporter Name/Other Supplier Name/Transporter Name/Other
</Label> </Label>
<input <input
type="text" type="text"
id="supplerName" id="payee"
className="form-control form-control-sm" className="form-control form-control-sm"
{...register("supplerName")} {...register("payee")}
/> />
{errors.supplerName && ( {errors.payee && (
<small className="danger-text"> <small className="danger-text">
{errors.supplerName.message} {errors.payee.message}
</small> </small>
)} )}
</div> </div>
<div className="col-md-6"> <div className="col-md-6">
<Label htmlFor="location" className="form-label" required> <Label htmlFor="currencyId" className="form-label" required>
Location Currency
</Label> </Label>
<input <select
type="text" id="currencyId"
id="location" className="form-select form-select-sm"
className="form-control form-control-sm" {...register("currencyId")}
{...register("location")} >
/> <option value="">Select Currency</option>
{errors.location && (
<small className="danger-text">{errors.location.message}</small> {currencyLoading && <option>Loading...</option>}
{!currencyLoading &&
!currencyError &&
currencyData?.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>
</div> </div>
<div className="row my-2 text-start"> <div className="row my-2 text-start">
@ -285,9 +304,33 @@ function ManagePaymentRequest({ closeModal, expenseToEdit = null }) {
{...register("title")} {...register("title")}
/> />
{errors.title && ( {errors.title && (
<small className="danger-text">{errors.title.message}</small> <small className="danger-text">
{errors.title.message}
</small>
)} )}
</div> </div>
<div className="col-md-6">
<Label htmlFor="isAdvancePayment" className="form-label" required>
Advance Payment
</Label>
<select
id="isAdvancePayment"
className="form-select form-select-sm"
{...register("isAdvancePayment", {
setValueAs: (v) => v === "true" ? true : v === "false" ? false : undefined,
})}
>
<option value="">Select Option</option>
<option value="true">True</option>
<option value="false">False</option>
</select>
{errors.isAdvancePayment && (
<small className="danger-text">{errors.isAdvancePayment.message}</small>
)}
</div>
</div> </div>
<div className="row my-2 text-start"> <div className="row my-2 text-start">

View File

@ -4,13 +4,15 @@ import showToast from "../services/toastService";
export const useCreatePaymentRequest = (onSuccessCallBack) => { export const useCreatePaymentRequest = (onSuccessCallBack) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async (payload) => { mutationFn: async (payload) => {
await PaymentRequestRepository.CreatePaymentRequest(payload); await PaymentRequestRepository.CreatePaymentRequest(payload);
}, },
onSuccess: (_, variables) => { onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: ["Expenses"] }); queryClient.invalidateQueries({ queryKey: ["PaymentRequest"] });
showToast("Expense Created Successfully", "success"); showToast("Payment Created Successfully", "success");
if (onSuccessCallBack) onSuccessCallBack(); if (onSuccessCallBack) onSuccessCallBack();
}, },
onError: (error) => { onError: (error) => {

View File

@ -689,3 +689,14 @@ export const useUpdateProjectLevelEmployeePermission = () => {
}, },
}); });
}; };
export const useCurrencies = () => {
return useQuery({
queryKey: ["currencies"],
queryFn: async () => {
const resp = await ProjectRepository.getCurrencies();
return resp.data;
},
});
};

View File

@ -1,4 +1,4 @@
import { z } from "zod"; import { boolean, z } from "zod";
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const ALLOWED_TYPES = [ const ALLOWED_TYPES = [
"application/pdf", "application/pdf",
@ -14,6 +14,9 @@ export const PaymentRequestSchema = (expenseTypes) => {
expenseCategoryId: z expenseCategoryId: z
.string() .string()
.min(1, { message: "Expense Category is required" }), .min(1, { message: "Expense Category is required" }),
currencyId: z
.string()
.min(1, { message: "Currency is required" }),
dueDate: z.string().min(1, { message: "Date is required" }), dueDate: z.string().min(1, { message: "Date is required" }),
description: z.string().min(1, { message: "Description is required" }), description: z.string().min(1, { message: "Description is required" }),
payee: z.string().min(1, { message: "Supplier name is required" }), payee: z.string().min(1, { message: "Supplier name is required" }),
@ -46,33 +49,7 @@ export const PaymentRequestSchema = (expenseTypes) => {
) )
.nonempty({ message: "At least one file attachment is required" }), .nonempty({ message: "At least one file attachment is required" }),
}) })
.refine( };
(data) => {
return (
!data.projectId || (data.paidById && data.paidById.trim() !== "")
);
},
{
message: "Please select who paid (employee)",
path: ["paidById"],
}
)
.superRefine((data, ctx) => {
const expenseType = expenseTypes.find(
(et) => et.id === data.expensesTypeId
);
if (
expenseType?.noOfPersonsRequired &&
(!data.noOfPersons || data.noOfPersons < 1)
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "No. of Persons is required and must be at least 1",
path: ["noOfPersons"],
});
}
});
};
export const defaultPaymentRequest = { export const defaultPaymentRequest = {
title:"", title:"",
@ -83,7 +60,7 @@ export const defaultPaymentRequest = {
dueDate: "", dueDate: "",
projectId: "", projectId: "",
expenseCategoryId: "", expenseCategoryId: "",
isAdvancePayment:"", isAdvancePayment:boolean,
billAttachments: [], billAttachments: [],
}; };

View File

@ -6,19 +6,19 @@ const ProjectRepository = {
api.get(`/api/project/details/${projetid}`), api.get(`/api/project/details/${projetid}`),
getProjectAllocation: (projectId, serviceId, organizationId, employeeStatus) => { getProjectAllocation: (projectId, serviceId, organizationId, employeeStatus) => {
let url = `/api/project/allocation/${projectId}`; let url = `/api/project/allocation/${projectId}`;
const params = []; const params = [];
if (organizationId) params.push(`organizationId=${organizationId}`); if (organizationId) params.push(`organizationId=${organizationId}`);
if (serviceId) params.push(`serviceId=${serviceId}`); if (serviceId) params.push(`serviceId=${serviceId}`);
if (employeeStatus !== undefined) params.push(`includeInactive=${employeeStatus}`); if (employeeStatus !== undefined) params.push(`includeInactive=${employeeStatus}`);
if (params.length > 0) { if (params.length > 0) {
url += `?${params.join("&")}`; url += `?${params.join("&")}`;
} }
return api.get(url); return api.get(url);
}, },
getEmployeesByProject: (projectId) => getEmployeesByProject: (projectId) =>
@ -97,6 +97,9 @@ const ProjectRepository = {
} }
return api.get(url); return api.get(url);
}, },
getCurrencies: () =>
api.get(`/api/Master/currencies/list`),
}; };
export const TasksRepository = { export const TasksRepository = {