diff --git a/src/dealer-claim/components/request-detail/claim-cards/ActivityInformationCard.tsx b/src/dealer-claim/components/request-detail/claim-cards/ActivityInformationCard.tsx index 49e8911..44e200d 100644 --- a/src/dealer-claim/components/request-detail/claim-cards/ActivityInformationCard.tsx +++ b/src/dealer-claim/components/request-detail/claim-cards/ActivityInformationCard.tsx @@ -18,11 +18,11 @@ interface ActivityInformationCardProps { updatedAt?: string | Date; } -export function ActivityInformationCard({ - activityInfo, +export function ActivityInformationCard({ + activityInfo, className, createdAt, - updatedAt + updatedAt }: ActivityInformationCardProps) { // Defensive check: Ensure activityInfo exists if (!activityInfo) { @@ -109,7 +109,7 @@ export function ActivityInformationCard({

- {activityInfo.estimatedBudget + {activityInfo.estimatedBudget ? formatCurrency(activityInfo.estimatedBudget) : 'TBD'}

@@ -147,23 +147,40 @@ export function ActivityInformationCard({ -
- {activityInfo.closedExpensesBreakdown.map((item: { description: string; amount: number }, index: number) => ( -
- {item.description} - - {formatCurrency(item.amount)} - -
- ))} -
- Total - - {formatCurrency( - activityInfo.closedExpensesBreakdown.reduce((sum: number, item: { description: string; amount: number }) => sum + item.amount, 0) - )} - -
+
+ + + + + + + + + + + {activityInfo.closedExpensesBreakdown.map((item: any, index: number) => ( + + + + + + + ))} + + + + + +
DescriptionBaseGSTTotal
+ {item.description} + {item.gstRate ? {item.gstRate}% GST : null} + {formatCurrency(item.amount)}{formatCurrency(item.gstAmt || 0)} + {formatCurrency(item.totalAmt || (item.amount + (item.gstAmt || 0)))} +
Final Claim Amount + {formatCurrency( + activityInfo.closedExpensesBreakdown.reduce((sum: number, item: any) => sum + (item.totalAmt || (item.amount + (item.gstAmt || 0))), 0) + )} +
)} @@ -175,8 +192,8 @@ export function ActivityInformationCard({ Description
-
diff --git a/src/dealer-claim/components/request-detail/claim-cards/ProcessDetailsCard.tsx b/src/dealer-claim/components/request-detail/claim-cards/ProcessDetailsCard.tsx index 94959f9..a2b6d39 100644 --- a/src/dealer-claim/components/request-detail/claim-cards/ProcessDetailsCard.tsx +++ b/src/dealer-claim/components/request-detail/claim-cards/ProcessDetailsCard.tsx @@ -26,6 +26,11 @@ interface DMSDetails { remarks?: string; createdByName?: string; createdAt?: string; + // PWC fields + irn?: string; + ackNo?: string; + ackDate?: string; + signedInvoiceUrl?: string; } interface ClaimAmountDetails { @@ -37,6 +42,8 @@ interface ClaimAmountDetails { interface CostBreakdownItem { description: string; amount: number; + gstAmt?: number; + totalAmt?: number; } interface RoleBasedVisibility { @@ -85,7 +92,7 @@ export function ProcessDetailsCard({ const calculateTotal = (items?: CostBreakdownItem[]) => { if (!items || items.length === 0) return 0; - return items.reduce((sum, item) => sum + (item.amount ?? 0), 0); + return items.reduce((sum, item) => sum + (item.totalAmt ?? (item.amount + (item.gstAmt ?? 0))), 0); }; // Don't render if nothing to show @@ -120,7 +127,7 @@ export function ProcessDetailsCard({

{ioDetails.ioNumber}

- + {ioDetails.remarks && (

Remark:

@@ -171,21 +178,54 @@ export function ProcessDetailsCard({
-

{dmsDetails.dmsNumber}

- + +
+
+

DMS Number

+

{dmsDetails.dmsNumber || 'N/A'}

+
+ {dmsDetails.ackNo && ( +
+

Ack No

+

{dmsDetails.ackNo}

+
+ )} +
+ + {dmsDetails.irn && ( +
+

IRN

+

+ {dmsDetails.irn} +

+
+ )} + + {dmsDetails.signedInvoiceUrl && ( + + )} + {dmsDetails.remarks && (
-

Remarks:

+

Remarks

{dmsDetails.remarks}

)}
-

By {dmsDetails.createdByName}

-

{formatDate(dmsDetails.createdAt)}

+

By {dmsDetails.createdByName}

+

{formatDate(dmsDetails.createdAt)}

)} @@ -241,10 +281,10 @@ export function ProcessDetailsCard({
{estimatedBudgetBreakdown.map((item, index) => ( -
- {item.description} - - {formatCurrency(item.amount)} +
+
{item.description}
+ + {formatCurrency(item.totalAmt ?? (item.amount + (item.gstAmt ?? 0)))}
))} @@ -269,10 +309,10 @@ export function ProcessDetailsCard({
{closedExpensesBreakdown.map((item, index) => ( -
- {item.description} - - {formatCurrency(item.amount)} +
+
{item.description}
+ + {formatCurrency(item.totalAmt ?? (item.amount + (item.gstAmt ?? 0)))}
))} diff --git a/src/dealer-claim/components/request-detail/claim-cards/ProposalDetailsCard.tsx b/src/dealer-claim/components/request-detail/claim-cards/ProposalDetailsCard.tsx index add4b72..21b0086 100644 --- a/src/dealer-claim/components/request-detail/claim-cards/ProposalDetailsCard.tsx +++ b/src/dealer-claim/components/request-detail/claim-cards/ProposalDetailsCard.tsx @@ -11,6 +11,12 @@ import { format } from 'date-fns'; interface ProposalCostItem { description: string; amount?: number | null; + gstRate?: number; + gstAmt?: number; + cgstAmt?: number; + sgstAmt?: number; + igstAmt?: number; + totalAmt?: number; } interface ProposalDetails { @@ -32,7 +38,7 @@ export function ProposalDetailsCard({ proposalDetails, className }: ProposalDeta if (proposalDetails.estimatedBudgetTotal !== undefined && proposalDetails.estimatedBudgetTotal !== null) { return proposalDetails.estimatedBudgetTotal; } - + // Calculate sum from costBreakup items if (proposalDetails.costBreakup && proposalDetails.costBreakup.length > 0) { const total = proposalDetails.costBreakup.reduce((sum, item) => { @@ -41,7 +47,7 @@ export function ProposalDetailsCard({ proposalDetails, className }: ProposalDeta }, 0); return total; } - + return 0; }; @@ -99,7 +105,13 @@ export function ProposalDetailsCard({ proposalDetails, className }: ProposalDeta Item Description - Amount + Base Amount + + + GST + + + Total @@ -107,16 +119,27 @@ export function ProposalDetailsCard({ proposalDetails, className }: ProposalDeta {(proposalDetails.costBreakup || []).map((item, index) => ( - {item.description} +
{item.description}
+ {item.gstRate ? ( +
+ {item.cgstAmt ? `CGST: ${item.gstRate / 2}%, SGST: ${item.gstRate / 2}%` : `IGST: ${item.gstRate}%`} +
+ ) : null} + + + {formatCurrency(item.amount)} + + + {formatCurrency(item.gstAmt)} - {formatCurrency(item.amount)} + {formatCurrency(item.totalAmt || (item.amount || 0) + (item.gstAmt || 0))} ))} - - Estimated Budget (Total) + + Estimated Budget (Total Inclusive of GST) {formatCurrency(estimatedTotal)} diff --git a/src/dealer-claim/components/request-detail/modals/DealerCompletionDocumentsModal.tsx b/src/dealer-claim/components/request-detail/modals/DealerCompletionDocumentsModal.tsx index cf2b833..c291e11 100644 --- a/src/dealer-claim/components/request-detail/modals/DealerCompletionDocumentsModal.tsx +++ b/src/dealer-claim/components/request-detail/modals/DealerCompletionDocumentsModal.tsx @@ -19,7 +19,7 @@ import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Badge } from '@/components/ui/badge'; import { CustomDatePicker } from '@/components/ui/date-picker'; -import { Upload, Plus, X, Calendar, FileText, Image, Receipt, CircleAlert, CheckCircle2, Eye, Download } from 'lucide-react'; +import { Upload, Plus, X, Calendar, FileText, Image, Receipt, CircleAlert, CheckCircle2, Eye, Download, IndianRupee } from 'lucide-react'; import { toast } from 'sonner'; import '@/components/common/FilePreview/FilePreview.css'; import './DealerCompletionDocumentsModal.css'; @@ -28,6 +28,19 @@ interface ExpenseItem { id: string; description: string; amount: number; + gstRate: number; + gstAmt: number; + cgstRate: number; + cgstAmt: number; + sgstRate: number; + sgstAmt: number; + igstRate: number; + igstAmt: number; + utgstRate: number; + utgstAmt: number; + cessRate: number; + cessAmt: number; + totalAmt: number; } interface DealerCompletionDocumentsModalProps { @@ -82,14 +95,14 @@ export function DealerCompletionDocumentsModal({ const canPreviewFile = (file: File): boolean => { const type = file.type.toLowerCase(); const name = file.name.toLowerCase(); - return type.includes('image') || - type.includes('pdf') || - name.endsWith('.pdf') || - name.endsWith('.jpg') || - name.endsWith('.jpeg') || - name.endsWith('.png') || - name.endsWith('.gif') || - name.endsWith('.webp'); + return type.includes('image') || + type.includes('pdf') || + name.endsWith('.pdf') || + name.endsWith('.jpg') || + name.endsWith('.jpeg') || + name.endsWith('.png') || + name.endsWith('.gif') || + name.endsWith('.webp'); }; // Cleanup object URLs when component unmounts or file changes @@ -107,12 +120,12 @@ export function DealerCompletionDocumentsModal({ toast.error('Preview is only available for images and PDF files'); return; } - + // Cleanup previous preview URL if (previewFile?.url) { URL.revokeObjectURL(previewFile.url); } - + const url = URL.createObjectURL(file); setPreviewFile({ file, url }); }; @@ -129,11 +142,43 @@ export function DealerCompletionDocumentsModal({ URL.revokeObjectURL(url); }; - // Calculate total closed expenses + // Calculate total closed expenses (inclusive of GST) const totalClosedExpenses = useMemo(() => { - return expenseItems.reduce((sum, item) => sum + (item.amount || 0), 0); + return expenseItems.reduce((sum, item) => sum + (item.totalAmt || item.amount || 0), 0); }, [expenseItems]); + // GST Calculation Helper + const calculateGST = (amount: number, rate: number, type: 'IGST' | 'CGST_SGST' = 'CGST_SGST') => { + const gstAmt = (amount * rate) / 100; + const totalAmt = amount + gstAmt; + + if (type === 'IGST') { + return { + gstAmt, + igstRate: rate, + igstAmt: gstAmt, + cgstRate: 0, + cgstAmt: 0, + sgstRate: 0, + sgstAmt: 0, + totalAmt + }; + } else { + const halfRate = rate / 2; + const halfAmt = gstAmt / 2; + return { + gstAmt, + igstRate: 0, + igstAmt: 0, + cgstRate: halfRate, + cgstAmt: halfAmt, + sgstRate: halfRate, + sgstAmt: halfAmt, + totalAmt + }; + } + }; + // Check if all required fields are filled const isFormValid = useMemo(() => { const hasCompletionDate = activityCompletionDate !== ''; @@ -150,15 +195,51 @@ export function DealerCompletionDocumentsModal({ const handleAddExpense = () => { setExpenseItems([ ...expenseItems, - { id: Date.now().toString(), description: '', amount: 0 }, + { + id: Date.now().toString(), + description: '', + amount: 0, + gstRate: 0, + gstAmt: 0, + cgstRate: 0, + cgstAmt: 0, + sgstRate: 0, + sgstAmt: 0, + igstRate: 0, + igstAmt: 0, + utgstRate: 0, + utgstAmt: 0, + cessRate: 0, + cessAmt: 0, + totalAmt: 0 + }, ]); }; - const handleExpenseChange = (id: string, field: 'description' | 'amount', value: string | number) => { + const handleExpenseChange = (id: string, field: string, value: any) => { setExpenseItems( - expenseItems.map((item) => - item.id === id ? { ...item, [field]: value } : item - ) + expenseItems.map((item) => { + if (item.id === id) { + const updatedItem = { ...item, [field]: value }; + + // Re-calculate GST if amount or rate changes + if (field === 'amount' || field === 'gstRate') { + const amount = field === 'amount' ? parseFloat(value) || 0 : item.amount; + const rate = field === 'gstRate' ? parseFloat(value) || 0 : item.gstRate; + const gst = calculateGST(amount, rate); + + return { + ...updatedItem, + amount, + gstRate: rate, + ...gst + }; + } + + return updatedItem; + } + return item; + }) ); }; @@ -201,7 +282,7 @@ export function DealerCompletionDocumentsModal({ if (validFiles.length > 0) { setCompletionDocuments([...completionDocuments, ...validFiles]); } - + if (completionDocsInputRef.current) completionDocsInputRef.current.value = ''; } }; @@ -243,7 +324,7 @@ export function DealerCompletionDocumentsModal({ if (validFiles.length > 0) { setActivityPhotos([...activityPhotos, ...validFiles]); } - + if (photosInputRef.current) photosInputRef.current.value = ''; } }; @@ -287,7 +368,7 @@ export function DealerCompletionDocumentsModal({ if (validFiles.length > 0) { setInvoicesReceipts([...invoicesReceipts, ...validFiles]); } - + if (invoicesInputRef.current) invoicesInputRef.current.value = ''; } }; @@ -371,7 +452,24 @@ export function DealerCompletionDocumentsModal({ setPreviewFile(null); setActivityCompletionDate(''); setNumberOfParticipants(''); - setExpenseItems([]); + setExpenseItems([{ + id: '1', + description: '', + amount: 0, + gstRate: 0, + gstAmt: 0, + cgstRate: 0, + cgstAmt: 0, + sgstRate: 0, + sgstAmt: 0, + igstRate: 0, + igstAmt: 0, + utgstRate: 0, + utgstAmt: 0, + cessRate: 0, + cessAmt: 0, + totalAmt: 0 + }]); setCompletionDocuments([]); setActivityPhotos([]); setInvoicesReceipts([]); @@ -391,696 +489,739 @@ export function DealerCompletionDocumentsModal({ }; return ( - - - - - - Activity Completion Documents - - - Step 5: Upload completion proof and final documents - -
-
- Dealer: {dealerName} -
-
- Activity: {activityName} -
-
- Please upload completion documents, photos, and provide details about the completed activity. -
-
-
- -
-
- {/* Activity Completion Date */} -
- - setActivityCompletionDate(date || '')} - maxDate={maxDate} - placeholderText="dd/mm/yyyy" - className="w-full max-w-[280px]" - wrapperClassName="max-w-[280px]" - /> -
- - {/* Closed Expenses Section */} -
-
-
-

Closed Expenses

- Optional + <> + + + + + + Activity Completion Documents + + + Step 5: Upload completion proof and final documents + +
+
+ Dealer: {dealerName} +
+
+ Activity: {activityName} +
+
+ Please upload completion documents, photos, and provide details about the completed activity.
-
-
- {expenseItems.map((item) => ( -
-
- - handleExpenseChange(item.id, 'description', e.target.value) - } - className="text-sm" - /> -
-
- - handleExpenseChange(item.id, 'amount', parseFloat(e.target.value) || 0) - } - className="text-sm" - /> + + +
+
+ {/* Activity Completion Date */} +
+ + setActivityCompletionDate(date || '')} + maxDate={maxDate} + placeholderText="dd/mm/yyyy" + className="w-full max-w-[280px]" + wrapperClassName="max-w-[280px]" + /> +
+ + {/* Closed Expenses Section */} +
+
+
+

Closed Expenses

+ Optional
- ))} - {expenseItems.length === 0 && ( -

- No expenses added. Click "Add Expense" to add expense items. -

- )} - {expenseItems.length > 0 && totalClosedExpenses > 0 && ( -
-
- Total Closed Expenses: - - ₹{totalClosedExpenses.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} - -
-
- )} -
-
+
+ {expenseItems.map((item) => ( +
+
+
+ + + handleExpenseChange(item.id, 'description', e.target.value) + } + className="w-full bg-white text-sm" + /> +
+
+ +
+ + + handleExpenseChange(item.id, 'amount', e.target.value) + } + className="w-full bg-white text-sm pl-8" + /> +
+
+
+ + + handleExpenseChange(item.id, 'gstRate', e.target.value) + } + className="w-full bg-white text-sm" + /> +
+ +
- {/* Completion Evidence Section */} -
-
-

Completion Evidence

- Required -
- - {/* Grid layout for Completion Documents and Activity Photos */} -
- {/* Completion Documents */} -
- -

- Upload documents proving activity completion (reports, certificates, etc.) - Can upload multiple files or ZIP folder -

-
0 - ? 'border-green-500 bg-green-50 hover:border-green-600' - : 'border-gray-300 hover:border-blue-500 bg-white' - }`} - > - documentPolicy.allowedFileTypes.includes(ext.replace('.', ''))).join(',')} - className="hidden" - id="completionDocs" - onChange={handleCompletionDocsChange} - /> - -
- {completionDocuments.length > 0 && ( -
-

- Selected Documents ({completionDocuments.length}): -

- {completionDocuments.map((file, index) => ( -
-
- - {file.name} -
-
- {canPreviewFile(file) && ( - - )} - - -
-
- ))} -
- )} -
- - {/* Activity Photos */} -
- -

- Upload photos from the completed activity (event photos, installations, etc.) -

-
0 - ? 'border-green-500 bg-green-50 hover:border-green-600' - : 'border-gray-300 hover:border-blue-500 bg-white' - }`} - > - documentPolicy.allowedFileTypes.includes(ext.replace('.', ''))).join(',')} - className="hidden" - id="completionPhotos" - onChange={handlePhotosChange} - /> - -
- {activityPhotos.length > 0 && ( -
-

- Selected Photos ({activityPhotos.length}): -

- {activityPhotos.map((file, index) => ( -
-
- - {file.name} -
-
- {canPreviewFile(file) && ( - - )} - - -
-
- ))} -
- )} -
-
-
- - {/* Supporting Documents Section */} -
-
-

Supporting Documents

- Optional -
- - {/* Grid layout for Invoices/Receipts and Attendance Sheet */} -
- {/* Invoices/Receipts */} -
- -

- Upload invoices and receipts for expenses incurred -

-
0 - ? 'border-blue-500 bg-blue-50 hover:border-blue-600' - : 'border-gray-300 hover:border-blue-500 bg-white' - }`} - > - documentPolicy.allowedFileTypes.includes(ext.replace('.', ''))).join(',')} - className="hidden" - id="invoiceReceipts" - onChange={handleInvoicesChange} - /> - -
- {invoicesReceipts.length > 0 && ( -
-

- Selected Documents ({invoicesReceipts.length}): -

- {invoicesReceipts.map((file, index) => ( -
-
- - {file.name} -
-
- {canPreviewFile(file) && ( - - )} - - -
-
- ))} -
- )} -
- - {/* Attendance Sheet */} -
- -

- Upload attendance records or participant lists (if applicable) -

-
- documentPolicy.allowedFileTypes.includes(ext.replace('.', ''))).join(',')} - className="hidden" - id="attendanceDoc" - onChange={handleAttendanceChange} - /> - -
- {attendanceSheet && ( -
-

- Selected Document: -

-
-
- - {attendanceSheet.name} -
-
- {canPreviewFile(attendanceSheet) && ( - + {item.gstAmt > 0 && ( +
+
+ CGST ({item.cgstRate}%) + ₹{item.cgstAmt.toLocaleString('en-IN')} +
+
+ SGST ({item.sgstRate}%) + ₹{item.sgstAmt.toLocaleString('en-IN')} +
+
+ GST Total + ₹{item.gstAmt.toLocaleString('en-IN')} +
+
+ Item Total + ₹{item.totalAmt.toLocaleString('en-IN')} +
+
)} - -
-
+ ))}
- )} -
-
-
- - {/* Completion Description */} -
- -