added updation invoice

This commit is contained in:
pramod.mahajan 2025-11-27 16:52:33 +05:30
parent 528255c2bd
commit 1b3e090211
9 changed files with 183 additions and 49 deletions

View File

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { AppFormProvider, useAppForm } from "../../hooks/appHooks/useAppForm"; import { AppFormProvider, useAppForm } from "../../hooks/appHooks/useAppForm";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { import {
@ -9,9 +9,13 @@ import {
import PurchasePartyDetails from "./PurchasePartyDetails"; import PurchasePartyDetails from "./PurchasePartyDetails";
import PurchaseTransportDetails from "./PurchaseTransportDetails"; import PurchaseTransportDetails from "./PurchaseTransportDetails";
import PurchasePaymentDetails from "./PurchasePaymentDetails"; import PurchasePaymentDetails from "./PurchasePaymentDetails";
import { useCreatePurchaseInvoice } from "../../hooks/usePurchase"; import {
useCreatePurchaseInvoice,
const ManagePurchase = ({ onClose }) => { usePurchase,
useUpdatePurchaseInvoice,
} from "../../hooks/usePurchase";
const ManagePurchase = ({ onClose, purchaseId }) => {
const { data } = usePurchase(purchaseId);
const [activeTab, setActiveTab] = useState(0); const [activeTab, setActiveTab] = useState(0);
const [completedTabs, setCompletedTabs] = useState([]); const [completedTabs, setCompletedTabs] = useState([]);
@ -20,7 +24,7 @@ const ManagePurchase = ({ onClose }) => {
name: "Party Details", name: "Party Details",
icon: "bx bx-user bx-md", icon: "bx bx-user bx-md",
subtitle: "Supplier & project information", subtitle: "Supplier & project information",
component: <PurchasePartyDetails />, component: <PurchasePartyDetails purchase={data} />,
}, },
{ {
name: "Invoice & Transport", name: "Invoice & Transport",
@ -40,10 +44,25 @@ const ManagePurchase = ({ onClose }) => {
resolver: zodResolver(PurchaseSchema), resolver: zodResolver(PurchaseSchema),
defaultValues: defaultPurchaseValue, defaultValues: defaultPurchaseValue,
mode: "onChange", mode: "onChange",
shouldUnregister: false,
}); });
const {reset} = purchaseOrder;
const handleNext = async () => { const {
reset,
formState: { errors },
} = purchaseOrder;
useEffect(() => {
if (purchaseId && data) {
reset(data);
setCompletedTabs([0, 1, 2]);
}
}, [purchaseId, data, reset]);
const handleNext = async (e) => {
e.preventDefault();
e.stopPropagation();
const currentStepFields = getStepFields(activeTab); const currentStepFields = getStepFields(activeTab);
const valid = await purchaseOrder.trigger(currentStepFields); const valid = await purchaseOrder.trigger(currentStepFields);
@ -53,18 +72,53 @@ const ManagePurchase = ({ onClose }) => {
} }
}; };
const handlePrev = () => { const handlePrev = (e) => {
e.preventDefault();
e.stopPropagation();
setActiveTab((prev) => Math.max(prev - 1, 0)); setActiveTab((prev) => Math.max(prev - 1, 0));
}; };
const { mutate: CreateInvoice, isPending } = useCreatePurchaseInvoice(() => { const { mutate: CreateInvoice, isPending } =
reset() useCreatePurchaseInvoice(() => {
reset();
onClose();
});
const { mutate: updatePurchase, isPending: isUpdating } =
useUpdatePurchaseInvoice(() => {
reset();
onClose(); onClose();
}); });
const onSubmit = (formData) => { const onSubmit = (formData) => {
let payload = formData; if (activeTab !== 2) {
CreateInvoice(payload); console.warn("Submit blocked - not on last step");
return;
}
const dirtyFields = purchaseOrder.formState.dirtyFields;
if (purchaseId) {
const changedData = Object.keys(dirtyFields).reduce((acc, key) => {
if (dirtyFields[key]) {
acc.push({
operationType: 0,
path: `/${key}`,
op: "replace",
from: null,
value: formData[key],
});
}
return acc;
}, []);
updatePurchase({
purchaseId,
payload: changedData,
});
} else {
CreateInvoice(formData);
}
}; };
return ( return (
@ -96,7 +150,9 @@ const ManagePurchase = ({ onClose }) => {
<span className="bs-stepper-label"> <span className="bs-stepper-label">
<span className="bs-stepper-title">{step.name}</span> <span className="bs-stepper-title">{step.name}</span>
<span className="bs-stepper-subtitle">{step.subtitle}</span> <span className="bs-stepper-subtitle">
{step.subtitle}
</span>
</span> </span>
</button> </button>
</div> </div>
@ -112,7 +168,23 @@ const ManagePurchase = ({ onClose }) => {
{/* Content */} {/* Content */}
<div className="bs-stepper-content py-2"> <div className="bs-stepper-content py-2">
<AppFormProvider {...purchaseOrder}> <AppFormProvider {...purchaseOrder}>
<form onSubmit={purchaseOrder.handleSubmit(onSubmit)}> <form
onKeyDown={(e) => {
if (e.key === "Enter" && activeTab !== 2) {
e.preventDefault();
}
}}
onSubmit={(e) => {
e.preventDefault();
if (activeTab !== 2) {
console.warn("BLOCKED SUBMIT on step:", activeTab);
return;
}
purchaseOrder.handleSubmit(onSubmit)(e);
}}
>
{stepsConfig[activeTab].component} {stepsConfig[activeTab].component}
<div className="d-flex justify-content-between mt-4"> <div className="d-flex justify-content-between mt-4">
@ -124,21 +196,27 @@ const ManagePurchase = ({ onClose }) => {
> >
Previous Previous
</button> </button>
<di>
<div>
{activeTab < stepsConfig.length - 1 ? ( {activeTab < stepsConfig.length - 1 ? (
<button <button
type="button" type="button"
className="btn btn-sm btn-primary" className="btn btn-sm btn-primary"
onClick={handleNext} onClick={handleNext}
disabled={isPending || isUpdating}
> >
Next <i className="bx bx-sm bx-right-arrow"></i> Next <i className="bx bx-sm bx-right-arrow"></i>
</button> </button>
) : ( ) : (
<button type="submit" className="btn btn-sm btn-primary"> <button
{isPending ? "Please Wait" : "SUbmit"} type="submit"
className="btn btn-sm btn-primary"
disabled={isPending || isUpdating}
>
{isPending || isUpdating ? "Please Wait" : "Submit"}
</button> </button>
)} )}
</di> </div>
</div> </div>
</form> </form>
</AppFormProvider> </AppFormProvider>
@ -148,3 +226,4 @@ const ManagePurchase = ({ onClose }) => {
}; };
export default ManagePurchase; export default ManagePurchase;

View File

@ -8,7 +8,7 @@ import { useDebounce } from "../../utils/appUtils";
import { usePurchaseContext } from "../../pages/purchase/PurchasePage"; import { usePurchaseContext } from "../../pages/purchase/PurchasePage";
const PurchaseList = ({ searchString }) => { const PurchaseList = ({ searchString }) => {
const { setViewPurchase } = usePurchaseContext(); const { setViewPurchase, setManagePurchase } = usePurchaseContext();
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const debounceSearch = useDebounce(searchString, 300); const debounceSearch = useDebounce(searchString, 300);
const { data, isLoading } = usePurchasesList( const { data, isLoading } = usePurchasesList(
@ -101,14 +101,22 @@ const PurchaseList = ({ searchString }) => {
}) })
} }
> >
<i className="bx bx-eye me-2"></i> <i className="bx bx-show me-2"></i>
<span className="align-left">view</span> <span className="align-left">view</span>
</a> </a>
</li> </li>
<li> <li>
<a className="dropdown-item cursor-pointer"> <a
<i className="bx bx-trash me-2"></i> className="dropdown-item cursor-pointer"
<span className="align-left">Add</span> onClick={() =>
setManagePurchase({
isOpen: true,
purchaseId: item.id,
})
}
>
<i className="bx bx-edit me-2"></i>
<span className="align-left">Edit</span>
</a> </a>
</li> </li>
<li> <li>

View File

@ -37,7 +37,7 @@ const PurchasePaymentDetails = () => {
id="baseAmount" id="baseAmount"
type="number" type="number"
className="form-control form-control-md" className="form-control form-control-md"
{...register("baseAmount")} {...register("baseAmount", { valueAsNumber: true })}
/> />
{errors?.baseAmount && ( {errors?.baseAmount && (
@ -56,7 +56,7 @@ const PurchasePaymentDetails = () => {
id="taxAmount" id="taxAmount"
type="number" type="number"
className="form-control form-control-md" className="form-control form-control-md"
{...register("taxAmount")} {...register("taxAmount",{ valueAsNumber: true })}
/> />
{errors?.taxAmount && ( {errors?.taxAmount && (
@ -75,7 +75,7 @@ const PurchasePaymentDetails = () => {
id="totalAmount" id="totalAmount"
type="number" type="number"
className="form-control form-control-md" className="form-control form-control-md"
{...register("totalAmount")} {...register("totalAmount",{ valueAsNumber: true })}
readOnly readOnly
/> />

View File

@ -49,7 +49,7 @@ export const PurchaseSchema = z.object({
proformaInvoiceNumber: z.string().nullable(), proformaInvoiceNumber: z.string().nullable(),
proformaInvoiceDate: z.coerce.date().nullable(), proformaInvoiceDate: z.coerce.date().nullable(),
proformaInvoiceAmount: z.string().nullable(), proformaInvoiceAmount: z.number().optional(),
invoiceNumber: z.string().nullable(), invoiceNumber: z.string().nullable(),
invoiceDate: z.coerce.date().nullable(), invoiceDate: z.coerce.date().nullable(),
@ -59,11 +59,11 @@ export const PurchaseSchema = z.object({
acknowledgmentDate: z.coerce.date().nullable(), acknowledgmentDate: z.coerce.date().nullable(),
acknowledgmentNumber: z.string().nullable(), acknowledgmentNumber: z.string().nullable(),
baseAmount: z.string().min(1, { message: "Base amount is required" }), baseAmount: z.number().min(1, { message: "Base amount is required" }),
taxAmount: z.string().min(1, { message: "Tax amount is required" }), taxAmount: z.number().min(1, { message: "Tax amount is required" }),
totalAmount: z.string().min(1, { message: "Total amount is required" }), totalAmount: z.number().min(1, { message: "Total amount is required" }),
paymentDueDate: z.coerce.date().nullable(), paymentDueDate: z.coerce.date().nullable(),
transportCharges: z.string().nullable(), transportCharges: z.number().nullable(),
description: z.string().min(1, { message: "Description is required" }), description: z.string().min(1, { message: "Description is required" }),
attachments: z.array(AttachmentSchema([], 25)).optional(), attachments: z.array(AttachmentSchema([], 25)).optional(),
@ -98,9 +98,9 @@ export const defaultPurchaseValue = {
acknowledgmentNumber: null, acknowledgmentNumber: null,
acknowledgmentDate: null, acknowledgmentDate: null,
baseAmount: "", baseAmount: 0,
taxAmount: "", taxAmount: 0,
totalAmount: "", totalAmount: 0,
paymentDueDate: null, paymentDueDate: null,
transportCharges: null, transportCharges: null,
description: "", description: "",

View File

@ -39,13 +39,13 @@ export const useOrganizationModal = () => {
// ================================Query============================================================= // ================================Query=============================================================
export const useGlobaleOrganizations = (pageSize, pageNumber, searchString) => { export const useGlobaleOrganizations = (pageSize, pageNumber,id, searchString) => {
return useQuery({ return useQuery({
queryKey: ["global_organization",pageSize, pageNumber, searchString], queryKey: ["global_organization",pageSize, pageNumber,id, searchString],
queryFn: async () => { queryFn: async () => {
const resp = await OrganizationRepository.getGlobalOrganization( const resp = await OrganizationRepository.getGlobalOrganization(
pageSize, pageSize,
pageNumber, pageNumber,id,
searchString searchString
); );
return resp.data; return resp.data;

View File

@ -62,3 +62,23 @@ export const useCreatePurchaseInvoice = (onSuccessCallback) => {
}, },
}); });
}; };
export const useUpdatePurchaseInvoice = (onSuccessCallback) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({purchaseId,payload}) => PurchaseRepository.UpdatePurchase(purchaseId, payload),
onSuccess: (data, variables) => {
showToast("Purchase Invoice Updated successfully", "success");
if (onSuccessCallback) onSuccessCallback();
},
onError: (error) => {
showToast(
error?.response?.data?.message ||
error.message ||
"Failed to create invoice",
"error"
);
},
});
};

View File

@ -18,7 +18,10 @@ export const usePurchaseContext = () => {
}; };
const PurchasePage = () => { const PurchasePage = () => {
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
const [addePurchase, setAddedPurchase] = useState(false); const [managePurchase, setManagePurchase] = useState({
isOpen: false,
purchaseId: null,
});
const [viewPurchaseState, setViewPurchase] = useState({ const [viewPurchaseState, setViewPurchase] = useState({
isOpen: false, isOpen: false,
purchaseId: null, purchaseId: null,
@ -26,8 +29,8 @@ const PurchasePage = () => {
const contextValue = { const contextValue = {
setViewPurchase, setViewPurchase,
setManagePurchase,
}; };
console.log(ViewPurchase);
return ( return (
<PurchaseContext.Provider value={contextValue}> <PurchaseContext.Provider value={contextValue}>
<div className="container-fluid"> <div className="container-fluid">
@ -56,7 +59,12 @@ const PurchasePage = () => {
<di className="col-6 text-end"> <di className="col-6 text-end">
<button <button
className="btn btn-sm btn-primary" className="btn btn-sm btn-primary"
onClick={() => setAddedPurchase(true)} onClick={() =>
setManagePurchase({
isOpen: true,
purchaseId: null,
})
}
> >
<i className="bx bx-plus-circle me-2"></i>Add <i className="bx bx-plus-circle me-2"></i>Add
</button> </button>
@ -65,20 +73,33 @@ const PurchasePage = () => {
</div> </div>
<PurchaseList searchString={searchText} /> <PurchaseList searchString={searchText} />
{addePurchase && ( {managePurchase.isOpen && (
<GlobalModel <GlobalModel
isOpen={addePurchase} isOpen={managePurchase.isOpen}
size="lg" size="lg"
closeModal={() => setAddedPurchase(false)} closeModal={() =>
setManagePurchase({
isOpen: false,
purchaseId: null,
})
}
> >
<ManagePurchase onClose={() => setAddedPurchase(false)} /> <ManagePurchase
onClose={() =>
setManagePurchase({
isOpen: false,
purchaseId: null,
})
}
purchaseId={managePurchase.purchaseId}
/>
</GlobalModel> </GlobalModel>
)} )}
{viewPurchaseState.isOpen && ( {viewPurchaseState.isOpen && (
<GlobalModel <GlobalModel
isOpen={viewPurchaseState.isOpen} isOpen={viewPurchaseState.isOpen}
size="lg" size="xl"
closeModal={() => closeModal={() =>
setViewPurchase({ isOpen: false, purchaseId: null }) setViewPurchase({ isOpen: false, purchaseId: null })
} }

View File

@ -41,9 +41,11 @@ const OrganizationRepository = {
return api.get(url); return api.get(url);
}, },
getGlobalOrganization: (pageSize, pageNumber, searchString) => getGlobalOrganization: (pageSize, pageNumber, searchString, id) =>
api.get( api.get(
`/api/Organization/list/basic?pageSize=${pageSize}&pageNumber=${pageNumber}&searchString=${searchString}` `/api/Organization/list/basic?pageSize=${pageSize}&pageNumber=${pageNumber}${
id ? `&id=${id}` : ""
}&searchString=${searchString}`
), ),
}; };

View File

@ -10,6 +10,10 @@ export const PurchaseRepository = {
GetPurchase: (id) => api.get(`/api/PurchaseInvoice/details/${id}`), GetPurchase: (id) => api.get(`/api/PurchaseInvoice/details/${id}`),
UpdatePurchase: (id, data) => UpdatePurchase: (id, data) =>
api.patch(`/api/PurchaseInvoice/edit/${id}`, data), api.patch(`/api/PurchaseInvoice/edit/${id}`, data),
GetDeliveryChallan: (purchaseInvoiceId) =>
api.get(`/api/PurchaseInvoice/delivery-challan/list/${purchaseInvoiceId}`),
addDelievryChallan: (data) =>
api.post(`/api/PurchaseInvoice/delivery-challan/create`, data),
}; };
// const filterPayload = JSON.stringify({ // const filterPayload = JSON.stringify({