added attachement in invoice

This commit is contained in:
pramod.mahajan 2025-11-28 20:49:47 +05:30
parent 9ce47a9232
commit f0d21b14a2
7 changed files with 110 additions and 79 deletions

View File

@ -3,7 +3,6 @@ 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 }) => {
debugger
return ( return (
<div className="d-flex flex-wrap gap-2 my-1"> <div className="d-flex flex-wrap gap-2 my-1">
{files {files
@ -42,7 +41,7 @@ const Filelist = ({ files, removeFile, expenseToEdit, sm = 6, md = 4 }) => {
role="button" role="button"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
removeFile(expenseToEdit ? file.documentId : idx); removeFile(expenseToEdit ? file.documentId ?? idx : idx);
}} }}
></i> ></i>
</Tooltip> </Tooltip>

View File

@ -0,0 +1,16 @@
import React from 'react'
const WarningBlock = ({content}) => {
return (
<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">
{content}
</p>
</div>
</div>
)
}
export default WarningBlock

View File

@ -19,6 +19,8 @@ import SelectField from "../common/Forms/SelectField";
import Filelist from "../Expenses/Filelist"; import Filelist from "../Expenses/Filelist";
import SingleFileUploader from "../common/SigleFileUploader"; import SingleFileUploader from "../common/SigleFileUploader";
import { localToUtc } from "../../utils/appUtils"; import { localToUtc } from "../../utils/appUtils";
import WarningBlock from "../InfoBlock/WarningBlock";
import { FILE_UPLOAD_INFO } from "../../utils/staticContent";
const DeliveryChallane = ({ purchaseId }) => { const DeliveryChallane = ({ purchaseId }) => {
const [file, setFile] = useState(null); const [file, setFile] = useState(null);
@ -178,15 +180,7 @@ const DeliveryChallane = ({ purchaseId }) => {
</div> </div>
</form> </form>
{!isUploaded && ( {!isUploaded && (
<div className="col-12 my-1"> <WarningBlock content={FILE_UPLOAD_INFO} />
<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>

View File

@ -65,17 +65,18 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
fileName: doc.fileName, fileName: doc.fileName,
base64Data: null, base64Data: null,
contentType: doc.contentType, contentType: doc.contentType,
documentId: doc.id, documentId: doc.documentId,
invoiceAttachmentTypeId:doc.invoiceAttachmentTypeId ?? null,
fileSize: 0, fileSize: 0,
description: "", description: "",
preSignedUrl: doc.preSignedUrl, preSignedUrl: doc.preSignedUrl,
isActive: doc.isActive ?? true, isActive: doc.isActive ?? true,
})) }))
: [] : [],
}); });
setCompletedTabs([0, 1, 2]); setCompletedTabs([0, 1, 2]);
} }
}, [purchaseId, data, reset]); }, []);
const handleNext = async (e) => { const handleNext = async (e) => {
e.preventDefault(); e.preventDefault();
@ -117,7 +118,8 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
if (purchaseId) { if (purchaseId) {
const changedData = Object.keys(dirtyFields).reduce((acc, key) => { const changedData = Object.keys(dirtyFields).reduce((acc, key) => {
if (dirtyFields[key]) { debugger;
if (dirtyFields[key] && key !== "invoiceAttachmentTypeId") {
acc.push({ acc.push({
operationType: 0, operationType: 0,
path: `/${key}`, path: `/${key}`,
@ -128,15 +130,16 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
} }
return acc; return acc;
}, []); }, []);
console.log(changedData)
updatePurchase({ // updatePurchase({
purchaseId, // purchaseId,
payload: changedData, // payload: changedData,
}); // });
} else { } else {
CreateInvoice(formData); CreateInvoice(formData);
} }
}; };
console.log(errors)
return ( return (
<div <div
id="wizard-property-listing" id="wizard-property-listing"

View File

@ -1,11 +1,21 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import Label from "../common/Label"; import Label from "../common/Label";
import { useAppFormContext } from "../../hooks/appHooks/useAppForm"; import {
AppFormController,
useAppFormContext,
} from "../../hooks/appHooks/useAppForm";
import DatePicker from "../common/DatePicker"; import DatePicker from "../common/DatePicker";
import { useInvoiceAttachmentTypes } from "../../hooks/masterHook/useMaster"; import { useInvoiceAttachmentTypes } from "../../hooks/masterHook/useMaster";
import Filelist from "../Expenses/Filelist"; import Filelist from "../Expenses/Filelist";
import SelectField from "../common/Forms/SelectField";
import { useWatch } from "react-hook-form";
import WarningBlock from "../InfoBlock/WarningBlock";
import { FILE_UPLOAD_INFO } from "../../utils/staticContent";
const PurchasePaymentDetails = ({ purchaseId = null }) => { const PurchasePaymentDetails = ({ purchaseId = null }) => {
const { data, isLoading, error, isError } = useInvoiceAttachmentTypes(); const { data, isLoading, error, isError } = useInvoiceAttachmentTypes();
const { data: InvoiceDocTypes, isLoading: invoiceDocLoading } =
useInvoiceAttachmentTypes();
const { const {
register, register,
watch, watch,
@ -25,7 +35,7 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
setValue("totalAmount", (base + tax).toFixed(2)); setValue("totalAmount", (base + tax).toFixed(2));
} }
}, [baseAmount, taxAmount, setValue]); }, [baseAmount, taxAmount, setValue]);
const invoiceAttachmentType = watch("invoiceAttachmentTypeId");
const files = watch("attachments"); const files = watch("attachments");
const onFileChange = async (e) => { const onFileChange = async (e) => {
const newFiles = Array.from(e.target.files); const newFiles = Array.from(e.target.files);
@ -43,6 +53,7 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
fileSize: file.size, fileSize: file.size,
description: "", description: "",
isActive: true, isActive: true,
invoiceAttachmentTypeId: invoiceAttachmentType,
}; };
}) })
); );
@ -72,16 +83,24 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
reader.onerror = (error) => reject(error); reader.onerror = (error) => reject(error);
}); });
const removeFile = (index) => { const removeFile = (index) => {
debugger
if (purchaseId) { if (purchaseId) {
const newFiles = files.map((file, i) => { const newFiles = files.map((file, i) => {
if (file.documentId !== index) return file; if (i !== index) return file;
return { return {
...file, ...file,
isActive: false, isActive: false,
}; };
}); });
setValue("attachments", newFiles, { shouldValidate: true });
console.log("OLD:", files);
console.log("NEW:", newFiles);
// setValue("attachments", newFiles, { shouldValidate: true });
const new_Files = files.filter((_, i) => i !== index);
setValue("attachments", new_Files, { shouldValidate: true });
} else { } else {
const newFiles = files.filter((_, i) => i !== index); const newFiles = files.filter((_, i) => i !== index);
setValue("attachments", newFiles, { shouldValidate: true }); setValue("attachments", newFiles, { shouldValidate: true });
@ -89,7 +108,7 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
}; };
return ( return (
<div className="row g-3 text-start"> <div className="row g-1 text-start">
<div className="col-12 col-md-4"> <div className="col-12 col-md-4">
<Label htmlFor="baseAmount" required> <Label htmlFor="baseAmount" required>
Base Amount Base Amount
@ -200,6 +219,31 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
</div> </div>
)} )}
</div> </div>
<div className="col-12">
<AppFormController
name="invoiceAttachmentTypeId"
control={control}
render={({ field }) => (
<SelectField
label="Select Document Type"
options={InvoiceDocTypes ?? []}
placeholder="Choose Type"
labelKeyKey="name"
valueKeyKey="id"
value={field.value}
onChange={field.onChange}
isLoading={invoiceDocLoading}
className="m-0"
/>
)}
/>
{errors?.invoiceAttachmentTypeId && (
<small className="danger-text">
{errors.invoiceAttachmentTypeId.message}
</small>
)}
</div>
<div className="text-start"> <div className="text-start">
<div className="col-md-12"> <div className="col-md-12">
@ -223,6 +267,7 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
id="attachments" id="attachments"
accept=".pdf,.jpg,.jpeg,.png" accept=".pdf,.jpg,.jpeg,.png"
multiple multiple
disabled={!invoiceAttachmentType}
style={{ display: "none" }} style={{ display: "none" }}
{...register("attachments")} {...register("attachments")}
onChange={(e) => { onChange={(e) => {
@ -232,9 +277,7 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
/> />
</div> </div>
{errors.attachments && ( {errors.attachments && (
<small className="danger-text"> <small className="danger-text">{errors.attachments.message}</small>
{errors.attachments.message}
</small>
)} )}
{files.length > 0 && ( {files.length > 0 && (
<Filelist <Filelist
@ -255,6 +298,9 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
} }
</div> </div>
))} ))}
{!invoiceAttachmentType && (
<WarningBlock content={FILE_UPLOAD_INFO} />
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -10,10 +10,9 @@ const ALLOWED_TYPES = [
]; ];
export const AttachmentSchema = z.object({ export const AttachmentSchema = z.object({
documentId: z.string().optional(), invoiceAttachmentTypeId: z.string().nullable(),
invoiceAttachmentTypeId: z.string().min(1, { message: "Attachment type is required" }),
fileName: z.string().min(1, { message: "Filename is required" }), fileName: z.string().min(1, { message: "Filename is required" }),
base64Data: z.string().min(1, { message: "File data is required" }), base64Data: z.string().nullable(),
contentType: z contentType: z
.string() .string()
.refine((val) => ALLOWED_TYPES.includes(val), { .refine((val) => ALLOWED_TYPES.includes(val), {
@ -25,39 +24,7 @@ export const AttachmentSchema = z.object({
description: z.string().optional().default(""), description: z.string().optional().default(""),
isActive: z.boolean().default(true), isActive: z.boolean().default(true),
}); });
// export const AttachmentSchema = (allowedContentType, maxSizeAllowedInMB) => {
// const allowedTypes = normalizeAllowedContentTypes(allowedContentType);
// return 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()
// .min(1, { message: "File data is required" }),
// contentType: z
// .string()
// .min(1, { message: "MIME type is required" })
// .refine(
// (val) => (allowedTypes.length ? allowedTypes.includes(val) : true),
// {
// message: `File type must be one of: ${allowedTypes.join(", ")}`,
// }
// ),
// fileSize: z
// .number()
// .int()
// .nonnegative("fileSize must be 0")
// .max(
// (maxSizeAllowedInMB ?? 25) * 1024 * 1024,
// `fileSize must be ${maxSizeAllowedInMB ?? 25}MB`
// ),
// description: z.string().optional().default(""),
// isActive: z.boolean(),
// });
// };
export const PurchaseSchema = z.object({ export const PurchaseSchema = z.object({
title: z.string().min(1, { message: "Title is required" }), title: z.string().min(1, { message: "Title is required" }),
@ -89,9 +56,10 @@ export const PurchaseSchema = z.object({
paymentDueDate: z.coerce.date().nullable(), paymentDueDate: z.coerce.date().nullable(),
transportCharges: z.number().nullable(), transportCharges: z.number().nullable(),
description: z.string().min(1, { message: "Description is required" }), description: z.string().min(1, { message: "Description is required" }),
invoiceAttachmentTypeId:z.string().nullable(),
attachments: z attachments: z
.array(AttachmentSchema) .array(AttachmentSchema)
.nonempty({ message: "At least one file attachment is required" }),
}); });
@ -130,9 +98,8 @@ export const defaultPurchaseValue = {
paymentDueDate: null, paymentDueDate: null,
transportCharges: null, transportCharges: null,
description: "", description: "",
invoiceAttachmentTypeId:null,
attachments: [], attachments: [],
// attachments: [],
}; };
export const getStepFields = (stepIndex) => { export const getStepFields = (stepIndex) => {
@ -165,7 +132,9 @@ export const getStepFields = (stepIndex) => {
"totalAmount", "totalAmount",
"transportCharges", "transportCharges",
"paymentDueDate", "paymentDueDate",
"invoiceAttachmentTypeId",
"description", "description",
"attachments"
], ],
}; };

View File

@ -0,0 +1,4 @@
export const FILE_UPLOAD_INFO = `
If you want to upload a document, please select a document type
before uploading the document.
`;