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";
const Filelist = ({ files, removeFile, expenseToEdit, sm = 6, md = 4 }) => {
debugger
return (
<div className="d-flex flex-wrap gap-2 my-1">
{files
@ -23,15 +22,15 @@ const Filelist = ({ files, removeFile, expenseToEdit, sm = 6, md = 4 }) => {
style={{ minWidth: "30px" }}
></i>
<Tooltip text={file.fileName} >
<Tooltip text={file.fileName}>
<div className="d-flex flex-column text-truncate">
<span className="fw-semibold small text-truncate">
{file.fileName}
</span>
<span className="text-body-secondary small">
{file.fileSize ? formatFileSize(file.fileSize) : ""}
</span>
</div>
<span className="fw-semibold small text-truncate">
{file.fileName}
</span>
<span className="text-body-secondary small">
{file.fileSize ? formatFileSize(file.fileSize) : ""}
</span>
</div>
</Tooltip>
</div>
@ -42,7 +41,7 @@ const Filelist = ({ files, removeFile, expenseToEdit, sm = 6, md = 4 }) => {
role="button"
onClick={(e) => {
e.preventDefault();
removeFile(expenseToEdit ? file.documentId : idx);
removeFile(expenseToEdit ? file.documentId ?? idx : idx);
}}
></i>
</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 SingleFileUploader from "../common/SigleFileUploader";
import { localToUtc } from "../../utils/appUtils";
import WarningBlock from "../InfoBlock/WarningBlock";
import { FILE_UPLOAD_INFO } from "../../utils/staticContent";
const DeliveryChallane = ({ purchaseId }) => {
const [file, setFile] = useState(null);
@ -178,15 +180,7 @@ const DeliveryChallane = ({ purchaseId }) => {
</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>
<WarningBlock content={FILE_UPLOAD_INFO} />
)}
</div>

View File

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

View File

@ -1,11 +1,21 @@
import React, { useEffect } from "react";
import Label from "../common/Label";
import { useAppFormContext } from "../../hooks/appHooks/useAppForm";
import {
AppFormController,
useAppFormContext,
} from "../../hooks/appHooks/useAppForm";
import DatePicker from "../common/DatePicker";
import { useInvoiceAttachmentTypes } from "../../hooks/masterHook/useMaster";
import Filelist from "../Expenses/Filelist";
const PurchasePaymentDetails = ({purchaseId=null}) => {
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 { data, isLoading, error, isError } = useInvoiceAttachmentTypes();
const { data: InvoiceDocTypes, isLoading: invoiceDocLoading } =
useInvoiceAttachmentTypes();
const {
register,
watch,
@ -25,7 +35,7 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
setValue("totalAmount", (base + tax).toFixed(2));
}
}, [baseAmount, taxAmount, setValue]);
const invoiceAttachmentType = watch("invoiceAttachmentTypeId");
const files = watch("attachments");
const onFileChange = async (e) => {
const newFiles = Array.from(e.target.files);
@ -43,6 +53,7 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
fileSize: file.size,
description: "",
isActive: true,
invoiceAttachmentTypeId: invoiceAttachmentType,
};
})
);
@ -72,16 +83,24 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
reader.onerror = (error) => reject(error);
});
const removeFile = (index) => {
debugger
if (purchaseId) {
const newFiles = files.map((file, i) => {
if (file.documentId !== index) return file;
if (i !== index) return file;
return {
...file,
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 {
const newFiles = files.filter((_, i) => i !== index);
setValue("attachments", newFiles, { shouldValidate: true });
@ -89,7 +108,7 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
};
return (
<div className="row g-3 text-start">
<div className="row g-1 text-start">
<div className="col-12 col-md-4">
<Label htmlFor="baseAmount" required>
Base Amount
@ -200,6 +219,31 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
</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="col-md-12">
@ -223,6 +267,7 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
id="attachments"
accept=".pdf,.jpg,.jpeg,.png"
multiple
disabled={!invoiceAttachmentType}
style={{ display: "none" }}
{...register("attachments")}
onChange={(e) => {
@ -232,9 +277,7 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
/>
</div>
{errors.attachments && (
<small className="danger-text">
{errors.attachments.message}
</small>
<small className="danger-text">{errors.attachments.message}</small>
)}
{files.length > 0 && (
<Filelist
@ -251,10 +294,13 @@ const PurchasePaymentDetails = ({purchaseId=null}) => {
(fileError?.fileSize?.message ||
fileError?.contentType?.message ||
fileError?.base64Data?.message,
fileError?.documentId?.message)
fileError?.documentId?.message)
}
</div>
))}
{!invoiceAttachmentType && (
<WarningBlock content={FILE_UPLOAD_INFO} />
)}
</div>
</div>
</div>

View File

@ -10,10 +10,9 @@ const ALLOWED_TYPES = [
];
export const AttachmentSchema = z.object({
documentId: z.string().optional(),
invoiceAttachmentTypeId: z.string().min(1, { message: "Attachment type is required" }),
invoiceAttachmentTypeId: z.string().nullable(),
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
.string()
.refine((val) => ALLOWED_TYPES.includes(val), {
@ -25,39 +24,7 @@ export const AttachmentSchema = z.object({
description: z.string().optional().default(""),
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({
title: z.string().min(1, { message: "Title is required" }),
@ -89,9 +56,10 @@ export const PurchaseSchema = z.object({
paymentDueDate: z.coerce.date().nullable(),
transportCharges: z.number().nullable(),
description: z.string().min(1, { message: "Description is required" }),
invoiceAttachmentTypeId:z.string().nullable(),
attachments: z
.array(AttachmentSchema)
.nonempty({ message: "At least one file attachment is required" }),
});
@ -130,9 +98,8 @@ export const defaultPurchaseValue = {
paymentDueDate: null,
transportCharges: null,
description: "",
invoiceAttachmentTypeId:null,
attachments: [],
// attachments: [],
};
export const getStepFields = (stepIndex) => {
@ -165,7 +132,9 @@ export const getStepFields = (stepIndex) => {
"totalAmount",
"transportCharges",
"paymentDueDate",
"invoiceAttachmentTypeId",
"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.
`;