Merge branch 'Purchase_Invoice_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Finance_Export_Functionality

This commit is contained in:
Kartik Sharma 2025-12-03 09:55:57 +05:30
commit 06ac8099ed
15 changed files with 886 additions and 309 deletions

View File

@ -205,7 +205,7 @@ const TaskReportList = () => {
id="total_pending_task" id="total_pending_task"
title="Total Pending Task" title="Total Pending Task"
content={ content={
<div className="text-wrap" style={{ maxWidth: "200px" }}> <div className="text-wrap" style={{ minWidth: "200px" }}>
This shows the total pending tasks for each activity on that date. This shows the total pending tasks for each activity on that date.
</div> </div>
} }

View File

@ -69,7 +69,7 @@ const ManageJobTicket = ({ Job }) => {
id="STATUS_CHANEG" id="STATUS_CHANEG"
Mode="click" Mode="click"
className="" className=""
align="right" align="left"
content={ content={
<ChangeStatus <ChangeStatus
statusId={data?.status?.id} statusId={data?.status?.id}
@ -149,7 +149,8 @@ const ManageJobTicket = ({ Job }) => {
<HoverPopup <HoverPopup
id="BRANCH_DETAILS" id="BRANCH_DETAILS"
Mode="click" Mode="click"
align="auto" align="right"
minWidth="340px"
boundaryRef={drawerRef} boundaryRef={drawerRef}
content={<BranchDetails branch={data?.projectBranch?.id} />} content={<BranchDetails branch={data?.projectBranch?.id} />}
> >

View File

@ -6,10 +6,6 @@ import {
togglePopup, togglePopup,
} from "../../slices/localVariablesSlice"; } from "../../slices/localVariablesSlice";
/**
* align: "auto" | "left" | "right"
* boundaryRef: optional ref to the drawer/container element to use as boundary
*/
const HoverPopup = ({ const HoverPopup = ({
id, id,
title, title,
@ -17,7 +13,9 @@ const HoverPopup = ({
children, children,
className = "", className = "",
Mode = "hover", Mode = "hover",
align = "auto", align = "auto", // <-- dynamic placement
minWidth = "250px",
maxWidth = "350px",
boundaryRef = null, boundaryRef = null,
}) => { }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -26,20 +24,15 @@ const HoverPopup = ({
const triggerRef = useRef(null); const triggerRef = useRef(null);
const popupRef = useRef(null); const popupRef = useRef(null);
const handleMouseEnter = () => { const handleMouseEnter = () => Mode === "hover" && dispatch(openPopup(id));
if (Mode === "hover") dispatch(openPopup(id)); const handleMouseLeave = () => Mode === "hover" && dispatch(closePopup(id));
};
const handleMouseLeave = () => {
if (Mode === "hover") dispatch(closePopup(id));
};
const handleClick = (e) => { const handleClick = (e) => {
if (Mode === "click") { if (Mode !== "click") return;
e.stopPropagation(); e.stopPropagation();
dispatch(togglePopup(id)); dispatch(togglePopup(id));
}
}; };
// Close on outside click when using click mode // Close popup when clicking outside (click mode)
useEffect(() => { useEffect(() => {
if (Mode !== "click" || !visible) return; if (Mode !== "click" || !visible) return;
@ -56,40 +49,68 @@ const HoverPopup = ({
document.addEventListener("click", handler); document.addEventListener("click", handler);
return () => document.removeEventListener("click", handler); return () => document.removeEventListener("click", handler);
}, [Mode, visible, dispatch, id]); }, [visible, Mode, id, dispatch]);
// ---------- DYNAMIC POSITIONING LOGIC ----------
useEffect(() => { useEffect(() => {
if (!visible || !popupRef.current || !triggerRef.current) return; if (!visible || !popupRef.current || !triggerRef.current) return;
requestAnimationFrame(() => { requestAnimationFrame(() => {
const popup = popupRef.current; const popup = popupRef.current;
const boundaryEl = (boundaryRef && boundaryRef.current) || popup.parentElement; const trigger = triggerRef.current;
if (!boundaryEl) return;
const boundaryEl =
(boundaryRef && boundaryRef.current) || popup.parentElement;
const boundaryRect = boundaryEl.getBoundingClientRect(); const boundaryRect = boundaryEl.getBoundingClientRect();
const triggerRect = triggerRef.current.getBoundingClientRect(); const triggerRect = trigger.getBoundingClientRect();
popup.style.left = "";
popup.style.right = "";
popup.style.transform = "";
popup.style.top = "";
const popupRect = popup.getBoundingClientRect(); const popupRect = popup.getBoundingClientRect();
const triggerCenterX = triggerRect.left + triggerRect.width / 2 - boundaryRect.left;
let left = triggerCenterX - popupRect.width / 2;
// Clamp to boundaries let left;
left = Math.max(0, Math.min(left, boundaryRect.width - popupRect.width));
// AUTO ALIGN (smart)
if (align === "auto") {
const center =
triggerRect.left +
triggerRect.width / 2 -
boundaryRect.left -
popupRect.width / 2;
left = Math.max(
0,
Math.min(center, boundaryRect.width - popupRect.width)
);
}
// LEFT ALIGN
else if (align === "left") {
left = triggerRect.left - boundaryRect.left;
if (left + popupRect.width > boundaryRect.width) {
left = boundaryRect.width - popupRect.width; // clamp right
}
}
// RIGHT ALIGN
else if (align === "right") {
left =
triggerRect.left +
triggerRect.width -
boundaryRect.left -
popupRect.width;
if (left < 0) left = 0; // clamp left
}
popup.style.left = `${left}px`; popup.style.left = `${left}px`;
popup.style.top = `100%`;
}); });
}, [visible, align, boundaryRef]); }, [visible, align, boundaryRef]);
return ( // ------------------------------------------------
<div
className="d-inline-block position-relative" // <-- ADD THIS !!
style={{ overflow: "visible" }}
>
<div
return (
<div className="d-inline-block position-relative" style={{ overflow: "visible" }}>
<div
ref={triggerRef} ref={triggerRef}
onMouseEnter={handleMouseEnter} onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave} onMouseLeave={handleMouseLeave}
@ -105,20 +126,18 @@ const HoverPopup = ({
className={`hover-popup bg-white border rounded shadow-sm p-3 position-absolute mt-2 ${className}`} className={`hover-popup bg-white border rounded shadow-sm p-3 position-absolute mt-2 ${className}`}
style={{ style={{
zIndex: 2000, zIndex: 2000,
top: "100%", minWidth,
minWidth: "200px", maxWidth,
maxWidth: "300px",
wordWrap: "break-word", wordWrap: "break-word",
}} }}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
{title && <h6 className="fw-semibold mb-2">{title}</h6>} {title && <h6 className="fw-semibold mb-2">{title}</h6>}
<div>{content}</div> {content}
</div> </div>
)} )}
</div> </div>
); );
}; };
export default HoverPopup; export default HoverPopup;

View File

@ -28,21 +28,21 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
const stepsConfig = useMemo( const stepsConfig = useMemo(
() => [ () => [
{ {
name: "Party Details", name: "Vendor & Project Details",
icon: "bx bx-user bx-md", icon: "bx bx-user bx-md",
subtitle: "Supplier & project information", subtitle: "Vendor information and project association",
component: <PurchasePartyDetails purchase={data} />, component: <PurchasePartyDetails purchase={data} />,
}, },
{ {
name: "Invoice & Transport", name: "Invoice & Logistics",
icon: "bx bx-receipt bx-md", icon: "bx bx-receipt bx-md",
subtitle: "Invoice, eWay bill & transport info", subtitle: "Invoice, e-Way bill, and logistics information",
component: <PurchaseTransportDetails />, component: <PurchaseTransportDetails />,
}, },
{ {
name: "Payment Details", name: "Invoice & Tax Amount",
icon: "bx bx-credit-card bx-md", icon: "bx bx-credit-card bx-md",
subtitle: "Amount, tax & due date", subtitle: "Payment terms, tax breakdown, and due dates",
component: <PurchasePaymentDetails purchaseId={purchaseId} />, component: <PurchasePaymentDetails purchaseId={purchaseId} />,
}, },
], ],
@ -127,8 +127,6 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
const onSubmit = useCallback( const onSubmit = useCallback(
(formData) => { (formData) => {
if (activeTab !== 2) return;
if (purchaseId) { if (purchaseId) {
const payload = generatePatchOps(formData); const payload = generatePatchOps(formData);
updatePurchase({ purchaseId, payload }); updatePurchase({ purchaseId, payload });
@ -136,7 +134,7 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
CreateInvoice(formData); CreateInvoice(formData);
} }
}, },
[activeTab, purchaseId, generatePatchOps, updatePurchase, CreateInvoice] [purchaseId, generatePatchOps, updatePurchase, CreateInvoice]
); );
return ( return (
<div className="bs-stepper horizontically mt-2 b-secondry shadow-none border-0"> <div className="bs-stepper horizontically mt-2 b-secondry shadow-none border-0">
@ -184,18 +182,10 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
{/* --- Form Content --- */} {/* --- Form Content --- */}
<div className="bs-stepper-content py-2 px-3"> <div className="bs-stepper-content py-2 px-3">
<AppFormProvider {...purchaseOrder}> <AppFormProvider {...purchaseOrder}>
<form {activeTab !== 2 && (
onSubmitCapture={(e) => { <div>
if (activeTab !== 2) {
e.preventDefault();
e.stopPropagation();
}
}}
onSubmit={purchaseOrder.handleSubmit(onSubmit)}
>
{stepsConfig[activeTab].component} {stepsConfig[activeTab].component}
{/* Buttons */}
<div className="d-flex justify-content-between mt-4"> <div className="d-flex justify-content-between mt-4">
<button <button
type="button" type="button"
@ -206,28 +196,39 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
Previous Previous
</button> </button>
<div>
{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 Next
</button> </button>
) : ( </div>
</div>
)}
{activeTab === 2 && (
<form onSubmit={purchaseOrder.handleSubmit(onSubmit)}>
{stepsConfig[2].component}
<div className="d-flex justify-content-between mt-4">
<button <button
type={activeTab == 2 ? "submit" : "button"} type="button"
className="btn btn-sm btn-outline-secondary"
onClick={handlePrev}
>
Previous
</button>
<button
type="submit"
className="btn btn-sm btn-primary" className="btn btn-sm btn-primary"
disabled={isPending || isUpdating} disabled={isPending || isUpdating}
> >
{isPending || isUpdating ? "Please Wait" : "Submit"} {isPending || isUpdating ? "Please Wait" : "Submit"}
</button> </button>
)}
</div>
</div> </div>
</form> </form>
)}
</AppFormProvider> </AppFormProvider>
</div> </div>
</div> </div>

View File

@ -0,0 +1,102 @@
export const getPurchaseActions = ({
item,
isActive,
canDelete,
canAddChallan,
setViewPurchase,
setManagePurchase,
setDeletingId,
setIsDeleteModalOpen,
setChallan,
setAddPayment,
}) => {
const actions = [];
// VIEW
actions.push({
key: "view",
label: "View",
icon: "bx bx-show",
show: true,
onClick: () =>
setViewPurchase({
isOpen: true,
purchaseId: item.id,
}),
});
if (!isActive) {
// EDIT
actions.push({
key: "edit",
label: "Edit",
icon: "bx bx-edit",
show: true,
onClick: () =>
setManagePurchase({
isOpen: true,
purchaseId: item.id,
}),
});
// DELETE
actions.push({
key: "delete",
label: "Delete",
icon: "bx bx-trash",
show: canDelete,
onClick: () => {
setDeletingId(item.id);
setIsDeleteModalOpen(true);
},
});
// ADD CHALLAN
actions.push({
key: "challan",
label: "Add Delivery Challan",
icon: "bx bx-file bx-plus",
show: canAddChallan,
onClick: () =>
setChallan({
isOpen: true,
purchaseId: item.id,
}),
});
// ADD PAYMENT
actions.push({
key: "payment",
label: "Add Payment",
icon: "bx bx-wallet",
show: true,
onClick: () =>
setAddPayment({
isOpen: true,
purchaseId: item.id,
}),
});
} else {
// RESTORE
actions.push({
key: "restore",
label: "Restore",
icon: "bx bx-undo",
show: true,
onClick: () => {
setDeletingId(item.id);
setIsDeleteModalOpen(true);
},
});
}
return actions.filter((a) => a.show);
};
export const DropdownItem = ({ icon, label, onClick }) => (
<li>
<a className="dropdown-item cursor-pointer" onClick={onClick}>
<i className={`${icon} me-2`}></i> {label}
</a>
</li>
);

View File

@ -1,21 +1,40 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { usePurchasesList } from "../../hooks/usePurchase"; import {
import { ITEMS_PER_PAGE } from "../../utils/constants"; useDeletePurchaseInvoice,
usePurchasesList,
} from "../../hooks/usePurchase";
import {
ADD_DELIVERY_CHALLAN,
DELETEPURCHASE_INVOICE,
ITEMS_PER_PAGE,
} from "../../utils/constants";
import Pagination from "../common/Pagination"; import Pagination from "../common/Pagination";
import { PurchaseColumn } from "./Purchasetable"; import { PurchaseColumn } from "./Purchasetable";
import { SpinnerLoader } from "../common/Loader"; import { SpinnerLoader } from "../common/Loader";
import { useDebounce } from "../../utils/appUtils"; import { useDebounce } from "../../utils/appUtils";
import { usePurchaseContext } from "../../pages/purchase/PurchasePage"; import { usePurchaseContext } from "../../pages/purchase/PurchasePage";
import ConfirmModal from "../common/ConfirmModal";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { DropdownItem, getPurchaseActions } from "./PurchaseActions";
const PurchaseList = ({ searchString }) => { const PurchaseList = ({ searchString, isActive }) => {
const { setViewPurchase, setManagePurchase, setChallan } = const { setViewPurchase, setManagePurchase, setChallan, setAddPayment } =
usePurchaseContext(); usePurchaseContext();
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const { mutate: DeletePurchaseInvoice, isPending } =
useDeletePurchaseInvoice();
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deletingId, setDeletingId] = useState(null);
const canAddChallan = useHasUserPermission(ADD_DELIVERY_CHALLAN);
const canDelete = useHasUserPermission(DELETEPURCHASE_INVOICE);
const debounceSearch = useDebounce(searchString, 300); const debounceSearch = useDebounce(searchString, 300);
const { data, isLoading } = usePurchasesList( const { data, isLoading } = usePurchasesList(
ITEMS_PER_PAGE, ITEMS_PER_PAGE,
currentPage, currentPage,
true, // true,
!isActive,
{}, {},
debounceSearch debounceSearch
); );
@ -28,9 +47,38 @@ const PurchaseList = ({ searchString }) => {
const visibleColumns = PurchaseColumn.filter((col) => !col.hidden); const visibleColumns = PurchaseColumn.filter((col) => !col.hidden);
const handleDeleteRestore = (id) => {
DeletePurchaseInvoice(
{ id, isActive: isActive }, // delete if active, restore if deleted
{
onSettled: () => {
setDeletingId(null);
setIsDeleteModalOpen(false);
},
}
);
};
return ( return (
<div className="card mt-2 page-min-h px-sm-4"> <>
<div className="table-responsive px-2"> {IsDeleteModalOpen && (
<ConfirmModal
isOpen={IsDeleteModalOpen}
type={!isActive ? "delete" : "undo"}
header={!isActive ? "Delete Invoice" : "Restore Invoice"}
message={
!isActive
? "Are you sure you want to delete?"
: "Are you sure you want to restore?"
}
onSubmit={handleDeleteRestore}
onClose={() => setIsDeleteModalOpen(false)}
loading={isPending}
paramData={deletingId}
/>
)}
<div className="card mt-2 page-min-h table-responsive px-sm-4">
<div className="px-2">
<table className="datatables-users table border-top text-nowrap"> <table className="datatables-users table border-top text-nowrap">
<thead> <thead>
<tr> <tr>
@ -47,18 +95,24 @@ const PurchaseList = ({ searchString }) => {
{/* LOADING */} {/* LOADING */}
{isLoading && ( {isLoading && (
<tr> <tr>
<td colSpan={visibleColumns.length + 1} className="border-0"> <td
<div className="py-6 py-12"> colSpan={visibleColumns.length + 1}
className="border-0"
style={{ height: "300px", verticalAlign: "middle" }}
>
<div className="d-flex justify-content-center align-items-center w-100 h-100">
<SpinnerLoader /> <SpinnerLoader />
</div> </div>
</td> </td>
</tr> </tr>
)} )}
{!isLoading && data?.data?.length === 0 && ( {!isLoading && data?.data?.length === 0 && (
<tr> <tr>
<td <td
colSpan={visibleColumns.length} colSpan={visibleColumns.length + 1}
className="text-center py-4 border-0" className="text-center border-0"
style={{ height: "400px", verticalAlign: "middle" }}
> >
No Data Found No Data Found
</td> </td>
@ -91,49 +145,25 @@ const PurchaseList = ({ searchString }) => {
></i> ></i>
</button> </button>
<ul className="dropdown-menu dropdown-menu-end"> <ul className="dropdown-menu dropdown-menu-end">
<li> {getPurchaseActions({
<a item,
className="dropdown-item cursor-pointer" isActive,
onClick={() => canDelete,
setViewPurchase({ canAddChallan,
isOpen: true, setViewPurchase,
purchaseId: item.id, setManagePurchase,
}) setDeletingId,
} setIsDeleteModalOpen,
> setChallan,
<i className="bx bx-show me-2"></i> setAddPayment,
<span className="align-left">view</span> }).map((action) => (
</a> <DropdownItem
</li> key={action.key}
<li> icon={action.icon}
<a label={action.label}
className="dropdown-item cursor-pointer" onClick={action.onClick}
onClick={() => />
setManagePurchase({ ))}
isOpen: true,
purchaseId: item.id,
})
}
>
<i className="bx bx-edit me-2"></i>
<span className="align-left">Edit</span>
</a>
</li>
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() =>
setChallan({
isOpen: true,
purchaseId: item.id,
})
}
>
<i className="bx bx-file bx-plus me-2"></i>
<span className="align-left">Add Delivery Challan</span>
</a>
</li>
</ul> </ul>
</div> </div>
</td> </td>
@ -151,6 +181,7 @@ const PurchaseList = ({ searchString }) => {
/> />
)} )}
</div> </div>
</>
); );
}; };

View File

@ -36,9 +36,7 @@ const PurchasePartyDetails = () => {
<input <input
id="title" id="title"
type="text" type="text"
className={`form-control form-control-xs ${ className={`form-control form-control-xs `}
errors?.title ? "is-invalid" : ""
}`}
{...register("title")} {...register("title")}
/> />
@ -73,15 +71,16 @@ const PurchasePartyDetails = () => {
control={control} control={control}
render={({ field }) => ( render={({ field }) => (
<SelectFieldSearch <SelectFieldSearch
{...field}
label="Organization" label="Organization"
placeholder="Select Organization" placeholder="Select Organization"
required required
value={field.value}
onChange={field.onChange}
valueKey="id" valueKey="id"
labelKey="name" labelKey="name"
useFetchHook={useGlobaleOrganizations} useFetchHook={useGlobaleOrganizations}
hookParams={[ITEMS_PER_PAGE, 1]} hookParams={[ITEMS_PER_PAGE, 1]}
error={errors?.organizationId?.message} errors={errors?.organizationId}
/> />
)} )}
/> />
@ -109,7 +108,7 @@ const PurchasePartyDetails = () => {
</div> </div>
<div className="col-12 col-md-6 my-0"> <div className="col-12 col-md-6 my-0">
<Label htmlFor="billingAddress">Billing Address</Label> <Label required htmlFor="billingAddress">Billing Address</Label>
<textarea <textarea
id="billingAddress" id="billingAddress"
@ -124,7 +123,7 @@ const PurchasePartyDetails = () => {
</div> </div>
<div className="col-12 col-md-6 my-0 mb-1"> <div className="col-12 col-md-6 my-0 mb-1">
<Label htmlFor="shippingAddress">Shipping Address</Label> <Label required htmlFor="shippingAddress">Shipping Address</Label>
<textarea <textarea
id="shippingAddress" id="shippingAddress"
@ -140,7 +139,7 @@ const PurchasePartyDetails = () => {
{/* Purchase Order Number */} {/* Purchase Order Number */}
<div className="col-12 col-md-6 "> <div className="col-12 col-md-6 ">
<Label htmlFor="purchaseOrderNumber" required> <Label htmlFor="purchaseOrderNumber" >
Purchase Order Number Purchase Order Number
</Label> </Label>
@ -160,7 +159,7 @@ const PurchasePartyDetails = () => {
{/* Purchase Order Date */} {/* Purchase Order Date */}
<div className="col-12 col-md-6 mb-1"> <div className="col-12 col-md-6 mb-1">
<Label htmlFor="purchaseOrderDate" required> <Label htmlFor="purchaseOrderDate" >
Purchase Order Date Purchase Order Date
</Label> </Label>

View File

@ -0,0 +1,298 @@
import React from "react";
import {
AppFormController,
AppFormProvider,
useAppForm,
} from "../../hooks/appHooks/useAppForm";
import { zodResolver } from "@hookform/resolvers/zod";
import { AddPurchasePayment, defaultPurchasePayment } from "./PurchaseSchema";
import { usePaymentAjustmentHead } from "../../hooks/masterHook/useMaster";
import { formatFigure, localToUtc } from "../../utils/appUtils";
import Label from "../common/Label";
import DatePicker from "../common/DatePicker";
import {
useAddPurchasePayment,
usePurchase,
usePurchasePaymentHistory,
} from "../../hooks/usePurchase";
import SelectField from "../common/Forms/SelectField";
import { SpinnerLoader } from "../common/Loader";
import { formatUTCToLocalTime } from "../../utils/dateUtils";
import Avatar from "../common/Avatar";
const PurchasePayment = ({onClose, purchaseId }) => {
const {
data: Purchase,
isLoading: isPurchaseLoading,
error: purchaseError,
} = usePurchase(purchaseId);
const methods = useAppForm({
resolver: zodResolver(AddPurchasePayment),
defaultValues: defaultPurchasePayment,
});
const {
control,
register,
handleSubmit,
reset,
formState: { errors },
} = methods;
const {
data: paymentTypes,
isLoading: isPaymentTypeLoading,
isError: isPaymentTypeError,
error: paymentError,
} = usePaymentAjustmentHead(true);
const { mutate: AddPayment, isPending } = useAddPurchasePayment(() => {
handleClose();
});
const { data, isLoading, isError, error } =
usePurchasePaymentHistory(purchaseId);
const onSubmit = (formData) => {
const payload = {
...formData,
paymentReceivedDate: localToUtc(formData.paymentReceivedDate),
invoiceId: purchaseId,
};
AddPayment(payload);
};
const handleClose = (formData) => {
reset(defaultPurchasePayment);
};
return (
<div className="contianer p-1">
<div className="text-center">
<p className="fs-5 text-semibod">Supplier / Vendor Transaction </p>
</div>
<div className="row g-3">
<div className="col-12 col-sm-6 px-2 p-sm-0">
<AppFormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)} className="p-0 text-start">
<div className="row px-md-1 px-0">
<div className="col-12 col-md-6 mb-2">
<Label required>TransanctionId</Label>
<input
type="text"
className="form-control form-control-md"
{...register("transactionId")}
/>
{errors.transactionId && (
<small className="danger-text">
{errors.transactionId.message}
</small>
)}
</div>
<div className="col-12 col-md-6 mb-2">
<Label required>Transaction Date </Label>
<DatePicker
className="w-100"
size="md"
name="paymentReceivedDate"
control={control}
minDate={
Purchase?.createdAt
? new Date(
new Date(Purchase?.createdAt).setDate(
new Date(Purchase?.createdAt).getDate() + 1
)
)
: null
}
maxDate={new Date()}
/>
{errors.paymentReceivedDate && (
<small className="danger-text">
{errors.paymentReceivedDate.message}
</small>
)}
</div>
<div className="col-12 col-md-6 mb-2">
<AppFormController
name="paymentAdjustmentHeadId"
control={control}
render={({ field }) => (
<SelectField
label="Payment Adjustment Head"
options={paymentTypes?.data ?? []}
placeholder="Choose a Status"
required
labelKeyKey="name"
valueKeyKey="id"
value={field.value}
onChange={field.onChange}
isLoading={isPaymentTypeLoading}
className="m-0"
/>
)}
/>
{errors.paymentAdjustmentHeadId && (
<small className="danger-text">
{errors.paymentAdjustmentHeadId.message}
</small>
)}
</div>
<div className="col-12 col-md-6 mb-2">
<Label htmlFor="amount" className="form-label" required>
Amount
</Label>
<input
type="number"
id="amount"
className="form-control form-control-md"
min="1"
step="0.01"
inputMode="decimal"
{...register("amount", { valueAsNumber: true })}
/>
{errors.amount && (
<small className="danger-text">
{errors.amount.message}
</small>
)}
</div>
<div className="col-12 mb-2">
<Label htmlFor="comment" className="form-label" required>
Comment
</Label>
<textarea
id="comment"
className="form-control form-control-sm"
{...register("comment")}
/>
{errors.comment && (
<small className="danger-text">
{errors.comment.message}
</small>
)}
</div>
<div className="d-flex justify-content-end gap-3">
{" "}
<button
type="reset"
className="btn btn-label-secondary btn-sm mt-3"
onClick={() => {
handleClose();
onClose();
}}
disabled={isPending}
>
Cancel
</button>
<button
type="submit"
className="btn btn-primary btn-sm mt-3"
disabled={isPending}
>
{isPending ? "Please Wait..." : "Submit"}
</button>
</div>
</div>
</form>
</AppFormProvider>
</div>
<div className="col-12 col-sm-6 px-2 p-sm-0">
<div className="d-flex flex-row gap-2 text-start">
<i className="bx bx-history"></i> <p>Purchase Payment Log</p>
</div>
{isLoading ? (
<SpinnerLoader />
) : (
data?.length > 0 && (
<div
className="row text-start mx-2"
style={{ maxHeight: "70vh", overflowY: "auto" }}
>
{data
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
.map((payment, index) => (
<div className="col-12 mb-2" key={payment.id}>
<div className=" p-2 border-start border-warning">
<div className="row">
<div className="col-12 col-md-6 d-flex justify-content-between align-items-center ">
<div>
<small className="fw-semibold me-1">
Transaction Date:
</small>{" "}
{formatUTCToLocalTime(
payment.paymentReceivedDate
)}
</div>
<span className="fs-semibold d-block d-md-none">
{formatFigure(payment.amount, {
type: "currency",
currency: "INR",
})}
</span>
</div>
<div className="col-12 col-md-6 mb-0 d-flex align-items-center m-0">
<small className="fw-semibold me-2">
Updated By:
</small>{" "}
<Avatar
size="xs"
firstName={payment?.createdBy?.firstName}
lastName={payment?.createdBy?.lastName}
/>{" "}
{payment?.createdBy?.firstName}{" "}
{payment.createdBy?.lastName}
</div>
</div>
<div className="row">
<div className="col-12 col-md-6">
<p className="mb-1">
<small className="fw-semibold">
Transaction ID:
</small>{" "}
{payment.transactionId}
</p>
</div>
<div className="col-12 ">
<div className="d-flex justify-content-between">
<span>
{payment?.paymentAdjustmentHead?.name}
</span>
<span className="fs-semibold d-none d-md-block">
{formatFigure(payment.amount, {
type: "currency",
currency: "INR",
})}
</span>
</div>
<p className="text-tiny m-0 mt-1">
{payment?.comment}
</p>
</div>
</div>
</div>
</div>
))}
</div>
)
)}
{data?.length === 0 && (
<div className="d-flex justify-content-center algin-items-center text-center">
<div>
<i className='bx bx-box'></i>
<p className="text-secondary">You don't have any payment yet.</p>
</div>
</div>
)}
</div>
</div>
</div>
);
};
export default PurchasePayment;

View File

@ -26,15 +26,16 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
const baseAmount = watch("baseAmount"); const baseAmount = watch("baseAmount");
const taxAmount = watch("taxAmount"); const taxAmount = watch("taxAmount");
const trCharge = watch("transportCharges");
useEffect(() => { useEffect(() => {
const base = parseFloat(baseAmount) || 0; const base = parseFloat(baseAmount) || 0;
const tax = parseFloat(taxAmount) || 0; const tax = parseFloat(taxAmount) || 0;
const transportCharges = parseFloat(trCharge) || 0;
if (base || tax) { if (base || tax || transportCharges) {
setValue("totalAmount", (base + tax).toFixed(2)); setValue("totalAmount", (base + tax + transportCharges).toFixed(2));
} }
}, [baseAmount, taxAmount, setValue]); }, [baseAmount, taxAmount, trCharge, setValue]);
const invoiceAttachmentType = watch("invoiceAttachmentTypeId"); const invoiceAttachmentType = watch("invoiceAttachmentTypeId");
const files = watch("attachments"); const files = watch("attachments");
const toBase64 = (file) => const toBase64 = (file) =>
@ -108,9 +109,7 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
return ( return (
<div className="row g-1 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">Base Amount</Label>
Base Amount
</Label>
<input <input
id="baseAmount" id="baseAmount"
@ -127,9 +126,7 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
</div> </div>
<div className="col-12 col-md-4"> <div className="col-12 col-md-4">
<Label htmlFor="taxAmount" required> <Label htmlFor="taxAmount">Tax Amount</Label>
Tax Amount
</Label>
<input <input
id="taxAmount" id="taxAmount"
@ -144,28 +141,7 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
</div> </div>
)} )}
</div> </div>
<div className="col-12 col-md-4"> <div className="col-12 col-md-4">
<Label htmlFor="totalAmount" required>
Total Amount
</Label>
<input
id="totalAmount"
type="number"
className="form-control form-control-xs"
{...register("totalAmount", { valueAsNumber: true })}
readOnly
/>
{errors?.totalAmount && (
<div className="small danger-text mt-1">
{errors.totalAmount.message}
</div>
)}
</div>
<div className="col-12 col-md-6">
<Label htmlFor="transportCharges">Transport Charges</Label> <Label htmlFor="transportCharges">Transport Charges</Label>
<input <input
@ -182,6 +158,23 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
)} )}
</div> </div>
<div className="col-12 col-md-6">
<Label htmlFor="totalAmount">Total Amount</Label>
<input
id="totalAmount"
type="number"
className="form-control form-control-xs"
{...register("totalAmount", { valueAsNumber: true })}
/>
{errors?.totalAmount && (
<div className="small danger-text mt-1">
{errors.totalAmount.message}
</div>
)}
</div>
<div className="col-12 col-md-6"> <div className="col-12 col-md-6">
<Label htmlFor="paymentDueDate">Payment Due Date</Label> <Label htmlFor="paymentDueDate">Payment Due Date</Label>
@ -200,9 +193,7 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
</div> </div>
<div className="col-12 mb-2"> <div className="col-12 mb-2">
<Label htmlFor="description" required> <Label htmlFor="description">Description</Label>
Description
</Label>
<textarea <textarea
id="description" id="description"

View File

@ -13,9 +13,7 @@ export const AttachmentSchema = z.object({
invoiceAttachmentTypeId: z.string().nullable(), invoiceAttachmentTypeId: z.string().nullable(),
fileName: z.string().min(1, { message: "Filename is required" }), fileName: z.string().min(1, { message: "Filename is required" }),
base64Data: z.string().nullable(), base64Data: z.string().nullable(),
contentType: z contentType: z.string().refine((val) => ALLOWED_TYPES.includes(val), {
.string()
.refine((val) => ALLOWED_TYPES.includes(val), {
message: "Only PDF, PNG, JPG, or JPEG files are allowed", message: "Only PDF, PNG, JPG, or JPEG files are allowed",
}), }),
fileSize: z.number().max(MAX_FILE_SIZE, { fileSize: z.number().max(MAX_FILE_SIZE, {
@ -23,10 +21,9 @@ 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),
documentId:z.string().nullable().default(null) documentId: z.string().nullable().default(null),
}); });
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" }),
projectId: z.string().min(1, { message: "Project is required" }), projectId: z.string().min(1, { message: "Project is required" }),
@ -51,20 +48,29 @@ 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.number().min(1, { message: "Base amount is required" }), baseAmount: z
taxAmount: z.number().min(1, { message: "Tax amount is required" }), .number()
totalAmount: z.number().min(1, { message: "Total amount is required" }), .or(z.nan()) // allow NaN
.transform((val) => (Number.isNaN(val) ? null : val)),
taxAmount: z
.number()
.or(z.nan()) // allow NaN
.transform((val) => (Number.isNaN(val) ? null : val)),
totalAmount: z
.number()
.or(z.nan()) // allow NaN
.transform((val) => (Number.isNaN(val) ? null : val)),
paymentDueDate: z.coerce.date().nullable(), paymentDueDate: z.coerce.date().nullable(),
transportCharges: z.number().nullable(), transportCharges: z
description: z.string().min(1, { message: "Description is required" }), .number()
.or(z.nan()) // allow NaN
.transform((val) => (Number.isNaN(val) ? null : val)),
description: z.string(),
invoiceAttachmentTypeId: z.string().nullable(), invoiceAttachmentTypeId: z.string().nullable(),
attachments: z attachments: z.array(AttachmentSchema),
.array(AttachmentSchema)
}); });
export const defaultPurchaseValue = { export const defaultPurchaseValue = {
title: "", title: "",
projectId: "", projectId: "",
@ -92,7 +98,7 @@ export const defaultPurchaseValue = {
taxAmount: 0, taxAmount: 0,
totalAmount: 0, totalAmount: 0,
paymentDueDate: null, paymentDueDate: null,
transportCharges: null, transportCharges: 0,
description: "", description: "",
invoiceAttachmentTypeId: null, invoiceAttachmentTypeId: null,
attachments: [], attachments: [],
@ -130,7 +136,7 @@ export const getStepFields = (stepIndex) => {
"paymentDueDate", "paymentDueDate",
"invoiceAttachmentTypeId", "invoiceAttachmentTypeId",
"description", "description",
"attachments" "attachments",
], ],
}; };
@ -172,12 +178,11 @@ export const DeliveryChallanSchema = z.object({
invoiceAttachmentTypeId: z.string().nullable(), invoiceAttachmentTypeId: z.string().nullable(),
deliveryChallanDate: z.string().min(1, { message: "Deliver date required" }), deliveryChallanDate: z.string().min(1, { message: "Deliver date required" }),
description: z.string().min(1, { message: "Description required" }), description: z.string().min(1, { message: "Description required" }),
attachment: z.any().refine( attachment: z
(val) => val && typeof val === "object" && !!val.base64Data, .any()
{ .refine((val) => val && typeof val === "object" && !!val.base64Data, {
message: "Please upload document", message: "Please upload document",
} }),
),
}); });
export const DeliveryChallanDefaultValue = { export const DeliveryChallanDefaultValue = {
@ -187,3 +192,20 @@ export const DeliveryChallanDefaultValue = {
attachment: null, attachment: null,
invoiceAttachmentTypeId: null, invoiceAttachmentTypeId: null,
}; };
export const AddPurchasePayment = z.object({
paymentReceivedDate: z.string().min(1, { message: "Date is required" }),
transactionId: z.string().min(1, "Transaction ID is required"),
amount: z.number().min(1, "Amount must be greater than zero"),
comment: z.string().min(1, { message: "Comment required" }),
paymentAdjustmentHeadId: z
.string()
.min(1, { message: "Payment Type required" }),
});
export const defaultPurchasePayment = {
paymentReceivedDate: null,
transactionId: "",
amount: 0,
comment: "",
paymentAdjustmentHeadId: "",
};

View File

@ -13,7 +13,7 @@ const PurchaseTransportDetails = () => {
<div className="row g-3 text-start"> <div className="row g-3 text-start">
{/* Invoice Number */} {/* Invoice Number */}
<div className="col-12 col-md-6"> <div className="col-12 col-md-6">
<Label htmlFor="invoiceNumber" required> <Label htmlFor="invoiceNumber" >
Invoice Number Invoice Number
</Label> </Label>

View File

@ -55,6 +55,17 @@ export const usePurchase = (id) => {
}); });
}; };
export const usePurchasePaymentHistory = (id) => {
return useQuery({
queryKey: ["purchase_payment_history", id],
queryFn: async () => {
const resp = await PurchaseRepository.GetPaymentHistory(id);
return resp.data;
},
enabled: !!id,
});
};
export const useCreatePurchaseInvoice = (onSuccessCallback) => { export const useCreatePurchaseInvoice = (onSuccessCallback) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -120,3 +131,51 @@ export const useAddDeliverChallan = (onSuccessCallback) => {
}, },
}); });
}; };
export const useAddPurchasePayment =(onSuccessCallback)=>{
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (payload) =>
PurchaseRepository.AddPayment(payload),
onSuccess: (data, variables) => {
queryClient.invalidateQueries({ queryKey: ["purchase_payment_history"] });
showToast("Payment added successfully", "success");
if (onSuccessCallback) onSuccessCallback();
},
onError: (error) => {
showToast(
error?.response?.data?.message ||
error.message ||
"Failed to Add payment",
"error"
);
},
});
}
export const useDeletePurchaseInvoice = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ id, isActive }) =>
await PurchaseRepository.deletePurchase(id, isActive),
onSuccess: (_, variable) => {
queryClient.invalidateQueries({ queryKey: ["purchase_list"] });
showToast(
`Purchase Invoice ${variable.isActive ? "restored" : "deleted"} successfully`,
"success"
);
},
onError: (error) => {
showToast(
error?.response?.data?.message ||
error.message ||
"Failed to delete branch",
"error"
);
},
});
};

View File

@ -6,6 +6,9 @@ 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"; import DeliveryChallane from "../../components/purchase/DeliveryChallane";
import PurchasePayment from "../../components/purchase/PurchasePayment";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { MANAGEPURCHASE_INVOICE } from "../../utils/constants";
export const PurchaseContext = createContext(); export const PurchaseContext = createContext();
export const usePurchaseContext = () => { export const usePurchaseContext = () => {
@ -19,6 +22,8 @@ export const usePurchaseContext = () => {
}; };
const PurchasePage = () => { const PurchasePage = () => {
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
const [showDelete, setShowDelete] = useState(false);
const [showInactive, setShowInactive] = useState(false);
const [addChallan, setChallan] = useState({ const [addChallan, setChallan] = useState({
isOpen: false, isOpen: false,
purchaseId: null, purchaseId: null,
@ -27,15 +32,22 @@ const PurchasePage = () => {
isOpen: false, isOpen: false,
purchaseId: null, purchaseId: null,
}); });
const [addPayment, setAddPayment] = useState({
isOpen: false,
purchaseId: null,
});
const [viewPurchaseState, setViewPurchase] = useState({ const [viewPurchaseState, setViewPurchase] = useState({
isOpen: false, isOpen: false,
purchaseId: null, purchaseId: null,
}); });
const canCreatePurchase = useHasUserPermission(MANAGEPURCHASE_INVOICE);
const contextValue = { const contextValue = {
setViewPurchase, setViewPurchase,
setManagePurchase, setManagePurchase,
setChallan, setChallan,
setAddPayment,
}; };
return ( return (
<PurchaseContext.Provider value={contextValue}> <PurchaseContext.Provider value={contextValue}>
@ -61,8 +73,24 @@ const PurchasePage = () => {
aria-controls="DataTables_Table_0" aria-controls="DataTables_Table_0"
/> />
</label> </label>
<div className="form-check form-switch d-inline-flex align-items-center ms-3">
<input
type="checkbox"
className="form-check-input"
id="inactiveEmployeesCheckbox"
checked={showInactive}
onChange={() => setShowInactive(!showInactive)}
/>
<label
htmlFor="inactiveEmployeesCheckbox"
className="ms-2 text-xs"
>
{!showInactive ? "Show Deleted" : "Hide Deleted"}
</label>
</div> </div>
<di className="col-sm-6 text-end"> </div>
<div className="col-sm-6 text-end">
{canCreatePurchase && (
<button <button
className="btn btn-sm btn-primary" className="btn btn-sm btn-primary"
onClick={() => onClick={() =>
@ -74,11 +102,12 @@ const PurchasePage = () => {
> >
<i className="bx bx-plus-circle me-2"></i>Add <i className="bx bx-plus-circle me-2"></i>Add
</button> </button>
</di> )}
</div>
</div> </div>
</div> </div>
<PurchaseList searchString={searchText} /> <PurchaseList searchString={searchText} isActive={showInactive} />
{managePurchase.isOpen && ( {managePurchase.isOpen && (
<GlobalModel <GlobalModel
isOpen={managePurchase.isOpen} isOpen={managePurchase.isOpen}
@ -115,7 +144,8 @@ const PurchasePage = () => {
)} )}
{addChallan.isOpen && ( {addChallan.isOpen && (
<GlobalModel size="xl" <GlobalModel
size="xl"
isOpen={addChallan.isOpen} isOpen={addChallan.isOpen}
closeModal={() => setChallan({ isOpen: false, purchaseId: null })} closeModal={() => setChallan({ isOpen: false, purchaseId: null })}
> >
@ -125,6 +155,21 @@ const PurchasePage = () => {
/> />
</GlobalModel> </GlobalModel>
)} )}
{addPayment.isOpen && (
<GlobalModel
size="xl"
isOpen={addPayment.isOpen}
closeModal={() =>
setAddPayment({ isOpen: false, purchaseId: null })
}
>
<PurchasePayment
onClose={() => setAddPayment({ isOpen: false, purchaseId: null })}
purchaseId={addPayment.purchaseId}
/>
</GlobalModel>
)}
</div> </div>
</PurchaseContext.Provider> </PurchaseContext.Provider>
); );

View File

@ -14,6 +14,12 @@ export const PurchaseRepository = {
api.get(`/api/PurchaseInvoice/delivery-challan/list/${purchaseInvoiceId}`), api.get(`/api/PurchaseInvoice/delivery-challan/list/${purchaseInvoiceId}`),
addDelievryChallan: (data) => addDelievryChallan: (data) =>
api.post(`/api/PurchaseInvoice/delivery-challan/create`, data), api.post(`/api/PurchaseInvoice/delivery-challan/create`, data),
AddPayment: (data) => api.post(`/api/PurchaseInvoice/add/payment`, data),
GetPaymentHistory: (purchaseInvoiceId) =>
api.get(`/api/PurchaseInvoice/payment-history/list/${purchaseInvoiceId}`),
deletePurchase: (id, isActive = false) =>
api.delete(`/api/PurchaseInvoice/delete/${id}?isActive=${isActive}`),
}; };
// const filterPayload = JSON.stringify({ // const filterPayload = JSON.stringify({

View File

@ -2,7 +2,6 @@ export const BASE_URL = process.env.VITE_BASE_URL;
// export const BASE_URL = "https://api.marcoaiot.com"; // export const BASE_URL = "https://api.marcoaiot.com";
export const THRESH_HOLD = 48; // hours export const THRESH_HOLD = 48; // hours
export const DURATION_TIME = 10; // minutes export const DURATION_TIME = 10; // minutes
export const ITEMS_PER_PAGE = 20; export const ITEMS_PER_PAGE = 20;
@ -66,8 +65,6 @@ export const PROCESS_EXPENSE = "ea5a1529-4ee8-4828-80ea-0e23c9d4dd11";
export const EXPENSE_MANAGE = "bdee29a2-b73b-402d-8dd1-c4b1f81ccbc3"; export const EXPENSE_MANAGE = "bdee29a2-b73b-402d-8dd1-c4b1f81ccbc3";
// --------------------------------Collection---------------------------- // --------------------------------Collection----------------------------
export const ADMIN_COLLECTION = "dbf17591-09fe-4c93-9e1a-12db8f5cc5de"; export const ADMIN_COLLECTION = "dbf17591-09fe-4c93-9e1a-12db8f5cc5de";
@ -76,6 +73,13 @@ export const CREATE_COLLECTION = "b93141fd-dbd3-4051-8f57-bf25d18e3555";
export const EDIT_COLLECTION = "455187b4-fef1-41f9-b3d0-025d0b6302c3"; export const EDIT_COLLECTION = "455187b4-fef1-41f9-b3d0-025d0b6302c3";
export const ADDPAYMENT_COLLECTION = "061d9ccd-85b4-4cb0-be06-2f9f32cebb72"; export const ADDPAYMENT_COLLECTION = "061d9ccd-85b4-4cb0-be06-2f9f32cebb72";
// --------------------Purchase Invoice--------------------------------
export const VIEWSELF_PURCHASEINVOICE = "91e09825-512a-465e-82ad-fa355b305585";
export const VIEWALL_PURCHASEINVOICE = "d6ae78d3-a941-4cc4-8d0a-d40479be4211";
export const MANAGEPURCHASE_INVOICE = "68ff925d-8ebf-4034-a137-8d3317c56ca1";
export const DELETEPURCHASE_INVOICE = "a4b77638-bf31-42bb-afd4-d5bbd15ccadc";
export const ADD_DELIVERY_CHALLAN = "a4b77638-bf31-42bb-afd4-d5bbd15ccadc";
// ----------------------------Tenant------------------------- // ----------------------------Tenant-------------------------
export const SUPPER_TENANT = "d032cb1a-3f30-462c-bef0-7ace73a71c0b"; export const SUPPER_TENANT = "d032cb1a-3f30-462c-bef0-7ace73a71c0b";
export const MANAGE_TENANTS = "00e20637-ce8d-4417-bec4-9b31b5e65092"; export const MANAGE_TENANTS = "00e20637-ce8d-4417-bec4-9b31b5e65092";
@ -98,7 +102,7 @@ export const EXPENSE_REJECTEDBY = [
]; ];
export const EXPENSE_DRAFT = "297e0d8f-f668-41b5-bfea-e03b354251c8"; export const EXPENSE_DRAFT = "297e0d8f-f668-41b5-bfea-e03b354251c8";
export const EXPENSE_MANAGEMENT = "a4e25142-449b-4334-a6e5-22f70e4732d7"; export const EXPENSE_MANAGEMENT = "a4e25142-449b-4334-a6e5-22f70e4732d7";
export const EXPENSE_CREATE = "b8586f67-dc19-49c3-b4af-224149efe1d3" export const EXPENSE_CREATE = "b8586f67-dc19-49c3-b4af-224149efe1d3";
export const INR_CURRENCY_CODE = "78e96e4a-7ce0-4164-ae3a-c833ad45ec2c"; export const INR_CURRENCY_CODE = "78e96e4a-7ce0-4164-ae3a-c833ad45ec2c";
export const EXPENSE_PROCESSED = "61578360-3a49-4c34-8604-7b35a3787b95"; export const EXPENSE_PROCESSED = "61578360-3a49-4c34-8604-7b35a3787b95";
export const TENANT_STATUS = [ export const TENANT_STATUS = [
@ -165,7 +169,7 @@ export const EXPENSE_STATUS = {
approve_pending: "4068007f-c92f-4f37-a907-bc15fe57d4d8", approve_pending: "4068007f-c92f-4f37-a907-bc15fe57d4d8",
payment_processed: "61578360-3a49-4c34-8604-7b35a3787b95", payment_processed: "61578360-3a49-4c34-8604-7b35a3787b95",
payment_done: "b8586f67-dc19-49c3-b4af-224149efe1d3", payment_done: "b8586f67-dc19-49c3-b4af-224149efe1d3",
} };
export const UUID_REGEX = export const UUID_REGEX =
/^\/employee\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/; /^\/employee\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
@ -184,7 +188,7 @@ export const FREQUENCY_FOR_RECURRING = {
2: "Half-Yearly", 2: "Half-Yearly",
3: "Yearly", 3: "Yearly",
4: "Daily", 4: "Daily",
5: "Weekly" 5: "Weekly",
}; };
export const PAYEE_RECURRING_EXPENSE = [ export const PAYEE_RECURRING_EXPENSE = [
@ -206,9 +210,8 @@ export const PAYEE_RECURRING_EXPENSE = [
}, },
]; ];
//#region Service Project and Jobs //#region Service Project and Jobs
export const STATUS_JOB_CLOSED = "3ddeefb5-ae3c-4e10-a922-35e0a452bb69" export const STATUS_JOB_CLOSED = "3ddeefb5-ae3c-4e10-a922-35e0a452bb69";
//#endregion //#endregion