Compare commits
3 Commits
339a5ff257
...
1c41c00d72
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c41c00d72 | |||
| b8d3e58428 | |||
| 1b3e090211 |
@ -234,7 +234,9 @@ font-weight: normal;
|
|||||||
.h-min { height: min-content; }
|
.h-min { height: min-content; }
|
||||||
.h-max { height: max-content; }
|
.h-max { height: max-content; }
|
||||||
|
|
||||||
|
.vh-50 {
|
||||||
|
height: 50vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import { formatFileSize, getIconByFileType } from "../../utils/appUtils";
|
import { formatFileSize, getIconByFileType } from "../../utils/appUtils";
|
||||||
import Tooltip from "../common/Tooltip";
|
import Tooltip from "../common/Tooltip";
|
||||||
|
|
||||||
const Filelist = ({ files, removeFile, expenseToEdit,sm=6,md=4 }) => {
|
const Filelist = ({ files, removeFile, expenseToEdit, sm = 6, md = 4 }) => {
|
||||||
return (
|
return (
|
||||||
<div className="d-flex flex-wrap gap-2 my-1">
|
<div className="d-flex flex-wrap gap-2 my-1">
|
||||||
{files
|
{files
|
||||||
@ -18,9 +18,7 @@ const Filelist = ({ files, removeFile, expenseToEdit,sm=6,md=4 }) => {
|
|||||||
{/* File icon and info */}
|
{/* File icon and info */}
|
||||||
<div className="d-flex align-items-center flex-grow-1 gap-2 overflow-hidden">
|
<div className="d-flex align-items-center flex-grow-1 gap-2 overflow-hidden">
|
||||||
<i
|
<i
|
||||||
className={`bx ${getIconByFileType(
|
className={`bx ${getIconByFileType(file?.contentType)} fs-3 `}
|
||||||
file?.contentType
|
|
||||||
)} fs-3 `}
|
|
||||||
style={{ minWidth: "30px" }}
|
style={{ minWidth: "30px" }}
|
||||||
></i>
|
></i>
|
||||||
|
|
||||||
@ -48,8 +46,7 @@ const Filelist = ({ files, removeFile, expenseToEdit,sm=6,md=4 }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -62,9 +59,7 @@ export const FilelistView = ({ files, viewFile }) => {
|
|||||||
<div className="row align-items-center">
|
<div className="row align-items-center">
|
||||||
{/* File icon and info */}
|
{/* File icon and info */}
|
||||||
<div className="col-12 d-flex align-items-center gap-2">
|
<div className="col-12 d-flex align-items-center gap-2">
|
||||||
<i
|
<i className={`bx ${getIconByFileType(file?.fileName)} fs-3`}></i>
|
||||||
className={`bx ${getIconByFileType(file?.fileName)} fs-3`}
|
|
||||||
></i>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="d-flex flex-column text-truncate"
|
className="d-flex flex-column text-truncate"
|
||||||
@ -93,3 +88,44 @@ export const FilelistView = ({ files, viewFile }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
export const FileView = ({ file, viewFile }) => {
|
||||||
|
if (!file) {
|
||||||
|
return (
|
||||||
|
<div className="d-flex justify-content-center align-items-center py-4">
|
||||||
|
<p className="mb-0 text-muted small">No file uploaded</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className=" bg-white ">
|
||||||
|
<div className="row align-items-center">
|
||||||
|
{/* File icon and info */}
|
||||||
|
<div className="col-12 d-flex align-items-center gap-2">
|
||||||
|
<i className={`bx ${getIconByFileType(file?.fileName)} fs-4`}></i>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="d-flex flex-column text-truncate"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
viewFile({
|
||||||
|
IsOpen: true,
|
||||||
|
Image: file.preSignedUrl,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="text-muted small text-truncate">
|
||||||
|
{file.fileName}
|
||||||
|
</span>
|
||||||
|
<span className="text-body-secondary small">
|
||||||
|
<Tooltip text={"Click on file"}>
|
||||||
|
{" "}
|
||||||
|
{file.fileSize ? formatFileSize(file.fileSize) : ""}
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
111
src/components/common/SigleFileUploader.jsx
Normal file
111
src/components/common/SigleFileUploader.jsx
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { useRef } from "react";
|
||||||
|
import Label from "./Label";
|
||||||
|
import Tooltip from "./Tooltip";
|
||||||
|
import { formatFileSize, getIconByFileType } from "../../utils/appUtils";
|
||||||
|
|
||||||
|
const SingleFileUploader = ({
|
||||||
|
label = "Upload Document",
|
||||||
|
required = false,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
onRemove,
|
||||||
|
disabled,
|
||||||
|
error,
|
||||||
|
accept = ".pdf,.jpg,.jpeg,.png",
|
||||||
|
maxSizeMB = 25,
|
||||||
|
hint = "(PDF, JPG, PNG, max 5MB)",
|
||||||
|
}) => {
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
|
||||||
|
const handleFileSelect = async (e) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
// Validate size
|
||||||
|
if (file.size > maxSizeMB * 1024 * 1024) {
|
||||||
|
alert(`File size cannot exceed ${maxSizeMB}MB`);
|
||||||
|
e.target.value = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to base64
|
||||||
|
const base64Data = await new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = () => resolve(reader.result.split(",")[1]);
|
||||||
|
reader.onerror = (err) => reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
const attachmentObj = {
|
||||||
|
fileName: file.name,
|
||||||
|
base64Data,
|
||||||
|
invoiceAttachmentTypeId: "", // set dynamically if needed
|
||||||
|
contentType: file.type,
|
||||||
|
fileSize: file.size,
|
||||||
|
description: "",
|
||||||
|
isActive: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
onChange(attachmentObj);
|
||||||
|
e.target.value = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="col-md-12">
|
||||||
|
<Label className="form-label" required={required}>
|
||||||
|
{label}
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="border border-secondary border-dashed rounded p-4 text-center bg-textMuted position-relative"
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onClick={() => inputRef.current.click()}
|
||||||
|
>
|
||||||
|
<i className="bx bx-cloud-upload d-block bx-lg"></i>
|
||||||
|
<span className="text-muted d-block">
|
||||||
|
Click to select or click here to browse
|
||||||
|
</span>
|
||||||
|
<small className="text-muted">{hint}</small>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
ref={inputRef}
|
||||||
|
accept={accept}
|
||||||
|
style={{ display: "none" }}
|
||||||
|
onChange={handleFileSelect}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && <small className="danger-text">{error}</small>}
|
||||||
|
|
||||||
|
{value && (
|
||||||
|
<div className="mt-3">
|
||||||
|
<div className="d-flex align-items-center justify-content-between bg-white border rounded p-2">
|
||||||
|
<div className="d-flex align-items-center gap-2">
|
||||||
|
<i
|
||||||
|
className={`bx ${getIconByFileType(value.contentType)} fs-3`}
|
||||||
|
></i>
|
||||||
|
|
||||||
|
<div className="d-flex flex-column text-truncate">
|
||||||
|
<span className="fw-semibold small text-truncate">
|
||||||
|
{value.fileName}
|
||||||
|
</span>
|
||||||
|
<small className="text-muted">
|
||||||
|
{value.fileSize ? formatFileSize(value.fileSize) : ""}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<i
|
||||||
|
className="bx bx-trash text-danger fs-5 cursor-pointer"
|
||||||
|
onClick={onRemove}
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SingleFileUploader;
|
||||||
68
src/components/purchase/DeliverChallanList.jsx
Normal file
68
src/components/purchase/DeliverChallanList.jsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useDeliverChallane } from "../../hooks/usePurchase";
|
||||||
|
import { SpinnerLoader } from "../common/Loader";
|
||||||
|
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||||
|
import { FileView } from "../Expenses/Filelist";
|
||||||
|
|
||||||
|
const DeliverChallanList = ({ purchaseId }) => {
|
||||||
|
const { data, isLoading, isError, error } = useDeliverChallane(purchaseId);
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="d-flex justify-content-center align-items-center text-center vh-50">
|
||||||
|
<SpinnerLoader />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<div className="py-3">
|
||||||
|
<Error error={error} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!isLoading && data.length === 0)
|
||||||
|
return (
|
||||||
|
<div className="d-flex justify-content-center align-items-center text-center vh-50">
|
||||||
|
<p className="mb-0">Not Yet</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="list-group list-group-flush text-start">
|
||||||
|
{data.map((item) => (
|
||||||
|
<div
|
||||||
|
key={item.id}
|
||||||
|
className="list-group-item d-flex justify-content-between align-items-start flex-wrap px-3 w-full"
|
||||||
|
>
|
||||||
|
{/* LEFT SIDE */}
|
||||||
|
<div className=" pe-3 text-break w-full">
|
||||||
|
<div className="d-flex justify-content-between">
|
||||||
|
<p className="mb-1 fw-semibold">{item.deliveryChallanNumber}</p>
|
||||||
|
<small className="text-muted text-end">
|
||||||
|
{formatUTCToLocalTime(item.deliveryChallanDate, true)}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-1 small text-muted text-break">
|
||||||
|
<span className="fw-semibold me-1">Invoice:</span>
|
||||||
|
{item.purchaseInvoice?.title} (
|
||||||
|
{item.purchaseInvoice?.purchaseInvoiceUId})
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="mb-1 text-break">
|
||||||
|
<span className="fw-semibold">Description:</span>{" "}
|
||||||
|
{item.description || "-"}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{item.attachment?.preSignedUrl && (
|
||||||
|
<FileView file={item.attachment} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeliverChallanList;
|
||||||
205
src/components/purchase/DeliveryChallane.jsx
Normal file
205
src/components/purchase/DeliveryChallane.jsx
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
useAddDeliverChallan,
|
||||||
|
useDeliverChallane,
|
||||||
|
} from "../../hooks/usePurchase";
|
||||||
|
import { SpinnerLoader } from "../common/Loader";
|
||||||
|
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||||
|
import DeliverChallanList from "./DeliverChallanList";
|
||||||
|
import { AppFormController, useAppForm } from "../../hooks/appHooks/useAppForm";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import {
|
||||||
|
DeliveryChallanDefaultValue,
|
||||||
|
DeliveryChallanSchema,
|
||||||
|
} from "./PurchaseSchema";
|
||||||
|
import Label from "../common/Label";
|
||||||
|
import DatePicker from "../common/DatePicker";
|
||||||
|
import { useInvoiceAttachmentTypes } from "../../hooks/masterHook/useMaster";
|
||||||
|
import SelectField from "../common/Forms/SelectField";
|
||||||
|
import Filelist from "../Expenses/Filelist";
|
||||||
|
import SingleFileUploader from "../common/SigleFileUploader";
|
||||||
|
import { localToUtc } from "../../utils/appUtils";
|
||||||
|
|
||||||
|
const DeliveryChallane = ({ purchaseId }) => {
|
||||||
|
const [file, setFile] = useState(null);
|
||||||
|
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
|
trigger,
|
||||||
|
watch,
|
||||||
|
reset,
|
||||||
|
formState: { errors },
|
||||||
|
} = useAppForm({
|
||||||
|
resolver: zodResolver(DeliveryChallanSchema),
|
||||||
|
defaultValues: DeliveryChallanDefaultValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data, isLoading } = useInvoiceAttachmentTypes();
|
||||||
|
const { mutate: AddChallan, isPending } = useAddDeliverChallan(() => {
|
||||||
|
setFile(null);
|
||||||
|
setValue("attachment", null, { shouldValidate: false });
|
||||||
|
reset({
|
||||||
|
...DeliveryChallanDefaultValue,
|
||||||
|
attachment: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const onSubmit = async (formData) => {
|
||||||
|
const valid = await trigger();
|
||||||
|
if (!valid) return;
|
||||||
|
const { invoiceAttachmentTypeId, ...rest } = formData;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
...rest,
|
||||||
|
attachment: formData.attachment
|
||||||
|
? {
|
||||||
|
...formData.attachment,
|
||||||
|
invoiceAttachmentTypeId,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
purchaseInvoiceId: purchaseId,
|
||||||
|
deliveryChallanDate: formData.deliveryChallanDate
|
||||||
|
? localToUtc(formData.deliveryChallanDate)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
AddChallan(payload);
|
||||||
|
};
|
||||||
|
const isUploaded = watch("attachment");
|
||||||
|
const isDocumentType = watch("invoiceAttachmentTypeId");
|
||||||
|
return (
|
||||||
|
<div className="row px-3 px-sm-1">
|
||||||
|
<div className="fw-semibold mb-3 fs-5">Delivery Challans</div>
|
||||||
|
|
||||||
|
<div className="col-md-6">
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
className="row g-3 text-start"
|
||||||
|
noValidate
|
||||||
|
>
|
||||||
|
{/* Challan Number */}
|
||||||
|
<div className="col-md-6">
|
||||||
|
<Label required>Delivery Challan No</Label>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control-sm"
|
||||||
|
{...register("deliveryChallanNumber")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{errors?.deliveryChallanNumber && (
|
||||||
|
<small className="danger-text">
|
||||||
|
{errors.deliveryChallanNumber.message}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-6">
|
||||||
|
<Label required>Delivery Date</Label>
|
||||||
|
|
||||||
|
<DatePicker
|
||||||
|
name="deliveryChallanDate"
|
||||||
|
control={control}
|
||||||
|
className="w-100"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{errors?.deliveryChallanDate && (
|
||||||
|
<small className="danger-text">
|
||||||
|
{errors.deliveryChallanDate.message}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-12">
|
||||||
|
<Label required>Description</Label>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
className="form-control form-control-sm"
|
||||||
|
rows="3"
|
||||||
|
{...register("description")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{errors?.description && (
|
||||||
|
<small className="danger-text">
|
||||||
|
{errors.description.message}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-12">
|
||||||
|
<AppFormController
|
||||||
|
name="invoiceAttachmentTypeId"
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<SelectField
|
||||||
|
label="Select Status"
|
||||||
|
options={data ?? []}
|
||||||
|
placeholder="Choose a Status"
|
||||||
|
labelKeyKey="name"
|
||||||
|
valueKeyKey="id"
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
isLoading={isLoading}
|
||||||
|
className="m-0"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{errors?.invoiceAttachmentTypeId && (
|
||||||
|
<small className="danger-text">
|
||||||
|
{errors.invoiceAttachmentTypeId.message}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SingleFileUploader
|
||||||
|
label="Upload Bill"
|
||||||
|
required
|
||||||
|
value={file}
|
||||||
|
onChange={(selectedFile) => {
|
||||||
|
setFile(selectedFile);
|
||||||
|
setValue("attachment", selectedFile, { shouldValidate: true });
|
||||||
|
}}
|
||||||
|
disabled={!isDocumentType}
|
||||||
|
onRemove={() => {
|
||||||
|
setFile(null);
|
||||||
|
setValue("attachment", null, { shouldValidate: true });
|
||||||
|
}}
|
||||||
|
error={errors?.attachment?.message}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="col-12 text-end">
|
||||||
|
<button type="submit" className="btn btn-sm btn-primary px-4">
|
||||||
|
{isPending ? "Please Wait..." : "Submit"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{!isUploaded && (
|
||||||
|
<div className="col-12 my-1">
|
||||||
|
<div className="d-flex align-items-center gap-2 p-3 rounded bg-warning bg-opacity-10 border border-warning-subtle text-start align-item-start">
|
||||||
|
<i className="bx bx-info-circle text-warning fs-5"></i>
|
||||||
|
<p className="mb-0 small">
|
||||||
|
If want upload document, Please select a document type before
|
||||||
|
uploading the document.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-6 text-start">
|
||||||
|
<div className="d-flex justiffy-content-start align-items-center gap-1">
|
||||||
|
<i className="bx bx-history bx-xs"></i>{" "}
|
||||||
|
<p className="fw-medium mb-0">History</p>
|
||||||
|
</div>
|
||||||
|
<div className="overflow-auto " style={{ maxHeight: "420px" }}>
|
||||||
|
<DeliverChallanList purchaseId={purchaseId} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeliveryChallane;
|
||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,8 @@ 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, setChallan } =
|
||||||
|
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,18 +102,34 @@ 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>
|
||||||
<a className="dropdown-item cursor-pointer">
|
<a
|
||||||
|
className="dropdown-item cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
setChallan({
|
||||||
|
isOpen: true,
|
||||||
|
purchaseId: item.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
<i className="bx bx-trash me-2"></i>
|
<i className="bx bx-trash me-2"></i>
|
||||||
<span className="align-left">Delete</span>
|
<span className="align-left">Delete</span>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -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
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@ -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: "",
|
||||||
@ -144,3 +144,49 @@ export const getStepFields = (stepIndex) => {
|
|||||||
|
|
||||||
return stepFieldMap[stepIndex] || [];
|
return stepFieldMap[stepIndex] || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SingleAttachmentSchema = z.object({
|
||||||
|
fileName: z.string().min(1, { message: "File name is required" }),
|
||||||
|
base64Data: z.string().min(1, { message: "File data is required" }),
|
||||||
|
invoiceAttachmentTypeId: z.string().nullable(),
|
||||||
|
|
||||||
|
contentType: z
|
||||||
|
.string()
|
||||||
|
.min(1, { message: "MIME type is required" })
|
||||||
|
.refine(
|
||||||
|
(val) =>
|
||||||
|
["application/pdf", "image/jpeg", "image/jpg", "image/png"].includes(
|
||||||
|
val
|
||||||
|
),
|
||||||
|
{
|
||||||
|
message: "File type must be PDF, JPG, JPEG or PNG",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
fileSize: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.nonnegative("fileSize must be ≥ 0")
|
||||||
|
.max(5 * 1024 * 1024, "File size must be ≤ 5MB"),
|
||||||
|
|
||||||
|
description: z.string().optional().default(""),
|
||||||
|
isActive: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeliveryChallanSchema = z.object({
|
||||||
|
deliveryChallanNumber: z
|
||||||
|
.string()
|
||||||
|
.min(1, { message: "Challan Number required" }),
|
||||||
|
invoiceAttachmentTypeId: z.string().nullable(),
|
||||||
|
deliveryChallanDate: z.string().min(1, { message: "Deliver date required" }),
|
||||||
|
description: z.string().min(1, { message: "Description required" }),
|
||||||
|
attachment: z.object(SingleAttachmentSchema.shape).nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeliveryChallanDefaultValue = {
|
||||||
|
deliveryChallanNumber: "",
|
||||||
|
deliveryChallanDate: "",
|
||||||
|
description: "",
|
||||||
|
attachment: null,
|
||||||
|
invoiceAttachmentTypeId: null,
|
||||||
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -31,6 +31,19 @@ export const usePurchasesList = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useDeliverChallane = (purchaseInvoiceId) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["deliverliy_challane", purchaseInvoiceId],
|
||||||
|
queryFn: async () => {
|
||||||
|
const resp = await PurchaseRepository.GetDeliveryChallan(
|
||||||
|
purchaseInvoiceId
|
||||||
|
);
|
||||||
|
return resp.data;
|
||||||
|
},
|
||||||
|
enabled: !!purchaseInvoiceId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const usePurchase = (id) => {
|
export const usePurchase = (id) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["purchase", id],
|
queryKey: ["purchase", id],
|
||||||
@ -62,3 +75,45 @@ 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"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const useAddDeliverChallan = (onSuccessCallback) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async (payload) =>
|
||||||
|
PurchaseRepository.addDelievryChallan(payload),
|
||||||
|
onSuccess: (data, variables) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["deliverliy_challane"] });
|
||||||
|
showToast("Challan added successfully", "success");
|
||||||
|
if (onSuccessCallback) onSuccessCallback();
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
showToast(
|
||||||
|
error?.response?.data?.message ||
|
||||||
|
error.message ||
|
||||||
|
"Failed to create invoice",
|
||||||
|
"error"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import GlobalModel from "../../components/common/GlobalModel";
|
|||||||
import ManagePurchase from "../../components/purchase/ManagePurchase";
|
import ManagePurchase from "../../components/purchase/ManagePurchase";
|
||||||
import PurchaseList from "../../components/purchase/PurchaseList";
|
import PurchaseList from "../../components/purchase/PurchaseList";
|
||||||
import ViewPurchase from "../../components/purchase/ViewPurchase";
|
import ViewPurchase from "../../components/purchase/ViewPurchase";
|
||||||
|
import DeliveryChallane from "../../components/purchase/DeliveryChallane";
|
||||||
|
|
||||||
export const PurchaseContext = createContext();
|
export const PurchaseContext = createContext();
|
||||||
export const usePurchaseContext = () => {
|
export const usePurchaseContext = () => {
|
||||||
@ -18,7 +19,14 @@ export const usePurchaseContext = () => {
|
|||||||
};
|
};
|
||||||
const PurchasePage = () => {
|
const PurchasePage = () => {
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
const [addePurchase, setAddedPurchase] = useState(false);
|
const [addChallan, setChallan] = useState({
|
||||||
|
isOpen: false,
|
||||||
|
purchaseId: null,
|
||||||
|
});
|
||||||
|
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 +34,9 @@ const PurchasePage = () => {
|
|||||||
|
|
||||||
const contextValue = {
|
const contextValue = {
|
||||||
setViewPurchase,
|
setViewPurchase,
|
||||||
|
setManagePurchase,
|
||||||
|
setChallan,
|
||||||
};
|
};
|
||||||
console.log(ViewPurchase);
|
|
||||||
return (
|
return (
|
||||||
<PurchaseContext.Provider value={contextValue}>
|
<PurchaseContext.Provider value={contextValue}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
@ -56,7 +65,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 +79,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 })
|
||||||
}
|
}
|
||||||
@ -86,6 +113,18 @@ const PurchasePage = () => {
|
|||||||
<ViewPurchase purchaseId={viewPurchaseState.purchaseId} />
|
<ViewPurchase purchaseId={viewPurchaseState.purchaseId} />
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{addChallan.isOpen && (
|
||||||
|
<GlobalModel size="xl"
|
||||||
|
isOpen={addChallan.isOpen}
|
||||||
|
closeModal={() => setChallan({ isOpen: false, purchaseId: null })}
|
||||||
|
>
|
||||||
|
<DeliveryChallane
|
||||||
|
purchaseId={addChallan.purchaseId}
|
||||||
|
onClose={() => setChallan({ isOpen: false, purchaseId: null })}
|
||||||
|
/>
|
||||||
|
</GlobalModel>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</PurchaseContext.Provider>
|
</PurchaseContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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}`
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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({
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user