diff --git a/src/dealer-claim/components/request-detail/WorkflowTab.tsx b/src/dealer-claim/components/request-detail/WorkflowTab.tsx index c020d18..27ae8c2 100644 --- a/src/dealer-claim/components/request-detail/WorkflowTab.tsx +++ b/src/dealer-claim/components/request-detail/WorkflowTab.tsx @@ -2137,8 +2137,9 @@ export function DealerClaimWorkflowTab({ const isDeptLeadStep = (stepLevelName && stepLevelName.toLowerCase().includes('department lead')) || (step.title && step.title.toLowerCase().includes('department lead')); - // Only show when step is approved and has IO details - return isDeptLeadStep && step.status === 'approved' && step.ioDetails && step.ioDetails.ioNumber && ( + // Only show when step is approved and has IO details. + // Hide this section for dealer users. + return isDeptLeadStep && !isDealer && step.status === 'approved' && step.ioDetails && step.ioDetails.ioNumber && (
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 1725e05..e623184 100644 --- a/src/dealer-claim/components/request-detail/claim-cards/ProcessDetailsCard.tsx +++ b/src/dealer-claim/components/request-detail/claim-cards/ProcessDetailsCard.tsx @@ -23,6 +23,8 @@ interface IODetails { interface DMSDetails { dmsNumber?: string; + eInvoiceNumber?: string; + eInvoiceDate?: string; remarks?: string; createdByName?: string; createdAt?: string; @@ -66,7 +68,6 @@ interface ProcessDetailsCardProps { } export function ProcessDetailsCard({ - ioDetails, dmsDetails, claimAmount, estimatedBudgetBreakdown, @@ -96,9 +97,18 @@ export function ProcessDetailsCard({ return items.reduce((sum, item) => sum + (item.totalAmt ?? (item.amount + (item.gstAmt ?? 0))), 0); }; + const hasInvoiceDetails = Boolean( + dmsDetails?.eInvoiceNumber || + dmsDetails?.ackNo || + dmsDetails?.eInvoiceDate || + dmsDetails?.dmsNumber || + dmsDetails?.ackDate || + dmsDetails?.irn || + dmsDetails?.signedInvoiceUrl + ); + // Don't render if nothing to show const hasContent = - (visibility.showIODetails && ioDetails) || (visibility.showDMSDetails && dmsDetails) || (dmsDetails?.creditNoteWfmData && dmsDetails.creditNoteWfmData.length > 0) || (visibility.showClaimAmount && claimAmount && claimAmount.amount !== undefined && claimAmount.amount !== null) || @@ -119,63 +129,8 @@ export function ProcessDetailsCard({ Workflow reference numbers - {/* IO Details - Only visible to internal RE users */} - {visibility.showIODetails && ioDetails && ( -
-
- - -
-

{ioDetails.ioNumber}

- - {ioDetails.remarks && ( -
-

Remark:

-

{ioDetails.remarks}

-
- )} - - {/* Budget Details */} - {(ioDetails.availableBalance !== undefined || ioDetails.blockedAmount !== undefined) && ( -
- {ioDetails.availableBalance !== undefined && ( -
- Available Balance: - - {formatCurrency(ioDetails.availableBalance)} - -
- )} - {ioDetails.blockedAmount !== undefined && ( -
- Blocked Amount: - - {formatCurrency(ioDetails.blockedAmount)} - -
- )} - {ioDetails.remainingBalance !== undefined && ( -
- Remaining Balance: - - {formatCurrency(ioDetails.remainingBalance)} - -
- )} -
- )} - -
-

By {ioDetails.blockedByName}

-

{formatDate(ioDetails.blockedAt)}

-
-
- )} - {/* E-Invoice Details */} - {visibility.showDMSDetails && dmsDetails && ( + {visibility.showDMSDetails && dmsDetails && hasInvoiceDetails && (
@@ -185,11 +140,34 @@ export function ProcessDetailsCard({
- - {dmsDetails.ackNo && ( + {(dmsDetails.eInvoiceNumber || dmsDetails.ackNo) && ( +
+

Invoice Number

+

{dmsDetails.eInvoiceNumber || dmsDetails.ackNo }

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

Invoice Date

+

{formatDate(dmsDetails.eInvoiceDate)}

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

DMS Number

+

{dmsDetails.dmsNumber}

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

Ack Date

+

{formatDate(dmsDetails.ackDate)}

+
+ )} + {dmsDetails.ackNo && dmsDetails.eInvoiceNumber && (

Ack No

-

{dmsDetails.ackNo}

+

{dmsDetails.ackNo}

)}
@@ -223,32 +201,69 @@ export function ProcessDetailsCard({ )}
-

By {dmsDetails.createdByName}

+

By {dmsDetails.createdByName || 'System'}

{formatDate(dmsDetails.createdAt)}

)} - {/* Credit Note Validation */} + {/* Credit Note Details */} {dmsDetails?.creditNoteWfmData && dmsDetails.creditNoteWfmData.length > 0 && (
{dmsDetails.creditNoteWfmData.map((cn: any, idx: number) => (
-
- Credit Note No: {cn.DOC_NO || 'N/A'} +
+ CN No: {cn.DOC_NO || 'N/A'} - {cn.MSG_TYP === 'E' ? 'Error' : (cn.MSG_TYP === 'S' ? 'Success' : cn.MSG_TYP || 'Unknown')} + {cn.MSG_TYP === 'E' ? 'Error' : (cn.MSG_TYP === 'S' ? 'Success' : cn.MSG_TYP || 'Processed')}
-
Txn No: {cn.TRNS_UNIQ_NO || 'N/A'}
-
{cn.MESSAGE || 'No Message'}
+ +
+
+ Txn No: + {cn.TRNS_UNIQ_NO || 'N/A'} +
+ {cn.TDS_AMT !== undefined && + cn.TDS_AMT !== null && + cn.TDS_AMT !== '' && + (Number.isNaN(Number(cn.TDS_AMT)) || Number(cn.TDS_AMT) !== 0) && ( +
+ TDS: + + {Number.isNaN(Number(cn.TDS_AMT)) + ? String(cn.TDS_AMT) + : formatCurrency(Number(cn.TDS_AMT))} + +
+ )} + {cn.CREDITED_TOTAL_AMT !== undefined && + cn.CREDITED_TOTAL_AMT !== null && + cn.CREDITED_TOTAL_AMT !== '' && + (Number.isNaN(Number(cn.CREDITED_TOTAL_AMT)) || Number(cn.CREDITED_TOTAL_AMT) !== 0) && ( +
+ Credit Amount: + + {Number.isNaN(Number(cn.CREDITED_TOTAL_AMT)) + ? String(cn.CREDITED_TOTAL_AMT) + : formatCurrency(Number(cn.CREDITED_TOTAL_AMT))} + +
+ )} +
+ + {cn.MESSAGE && ( +
+ {cn.MESSAGE} +
+ )}
))}
diff --git a/src/hooks/useRequestDetails.ts b/src/hooks/useRequestDetails.ts index 6a793d1..9315157 100644 --- a/src/hooks/useRequestDetails.ts +++ b/src/hooks/useRequestDetails.ts @@ -303,6 +303,7 @@ export function useRequestDetails( id: wf.requestNumber || wf.requestId, requestId: wf.requestId, // UUID for API calls requestNumber: wf.requestNumber, // Human-readable number for display + initiatorId: wf.initiatorId || wf.initiator_id, title: wf.title, description: wf.description, status: statusMap(wf.status), @@ -318,6 +319,7 @@ export function useRequestDetails( // Backend provides full SLA in summary.sla with all required fields sla: summary?.sla || wf.sla || null, initiator: { + userId: wf.initiator?.userId || wf.initiator?.user_id || wf.initiatorId || wf.initiator_id, name: wf.initiator?.displayName || wf.initiator?.email, role: wf.initiator?.designation || undefined, department: wf.initiator?.department || undefined, diff --git a/src/utils/claimDataMapper.ts b/src/utils/claimDataMapper.ts index b43bd8e..760ee9d 100644 --- a/src/utils/claimDataMapper.ts +++ b/src/utils/claimDataMapper.ts @@ -231,7 +231,25 @@ export function mapToClaimManagementRequest( // Map proposal details const expectedCompletionDate = proposalDetails?.expectedCompletionDate || proposalDetails?.expected_completion_date; - const proposal = proposalDetails ? { + const hasProposalData = Boolean( + (proposalDetails && Object.keys(proposalDetails).length > 0) && + ( + (Array.isArray(proposalDetails.costItems || proposalDetails.cost_items) && + (proposalDetails.costItems || proposalDetails.cost_items).length > 0) || + (Array.isArray(proposalDetails.costBreakup || proposalDetails.cost_breakup) && + (proposalDetails.costBreakup || proposalDetails.cost_breakup).length > 0) || + proposalDetails.proposalDocumentUrl || + proposalDetails.proposal_document_url || + proposalDetails.totalEstimatedBudget || + proposalDetails.total_estimated_budget || + proposalDetails.expectedCompletionDate || + proposalDetails.expected_completion_date || + proposalDetails.submittedAt || + proposalDetails.submitted_at + ) + ); + + const proposal = hasProposalData ? { proposalDocumentUrl: proposalDetails.proposalDocumentUrl || proposalDetails.proposal_document_url, costBreakup: Array.isArray(proposalDetails.costItems || proposalDetails.cost_items) ? (proposalDetails.costItems || proposalDetails.cost_items).map((item: any) => ({ @@ -259,9 +277,10 @@ export function mapToClaimManagementRequest( totalEstimatedBudget: proposalDetails.totalEstimatedBudget || proposalDetails.total_estimated_budget || 0, timelineMode: proposalDetails.timelineMode || proposalDetails.timeline_mode, expectedCompletionDate: expectedCompletionDate, + timelineForClosure: expectedCompletionDate, expectedCompletionDays: proposalDetails.expectedCompletionDays || proposalDetails.expected_completion_days, - timelineForClosure: expectedCompletionDate, // Map expectedCompletionDate to timelineForClosure for ProposalDetailsCard dealerComments: proposalDetails.dealerComments || proposalDetails.dealer_comments, + submittedAt: proposalDetails.submittedAt || proposalDetails.submitted_at || proposalDetails.submittedOn, submittedOn: proposalDetails.submittedAt || proposalDetails.submitted_at || proposalDetails.submittedOn, } : undefined; @@ -277,29 +296,47 @@ export function mapToClaimManagementRequest( }; // Map DMS details from new invoice and credit note tables + // Invoice could be top-level or nested in some backend responses + const inv = apiRequest.invoice || apiRequest.claimInvoice || apiRequest.claim_invoice || + apiRequest.claimRequest?.invoice || apiRequest.request?.invoice || invoice || {}; + const claimDetailsObj = claimDetails || {}; + const dmsDetails = { - eInvoiceNumber: invoice.invoiceNumber || invoice.invoice_number || - claimDetails.eInvoiceNumber || claimDetails.e_invoice_number, - eInvoiceDate: invoice.invoiceDate || invoice.invoice_date || - claimDetails.eInvoiceDate || claimDetails.e_invoice_date, - dmsNumber: invoice.dmsNumber || invoice.dms_number || - claimDetails.dmsNumber || claimDetails.dms_number, + eInvoiceNumber: inv.invoiceNumber || inv.invoice_number || + claimDetailsObj.eInvoiceNumber || claimDetailsObj.e_invoice_number || + inv.dmsNumber || inv.dms_number, + eInvoiceDate: inv.invoiceDate || inv.invoice_date || + claimDetailsObj.eInvoiceDate || claimDetailsObj.e_invoice_date, + dmsNumber: inv.dmsNumber || inv.dms_number || + claimDetailsObj.dmsNumber || claimDetailsObj.dms_number, creditNoteNumber: creditNote.creditNoteNumber || creditNote.credit_note_number || - claimDetails.creditNoteNumber || claimDetails.credit_note_number, + claimDetailsObj.creditNoteNumber || claimDetailsObj.credit_note_number, creditNoteDate: creditNote.creditNoteDate || creditNote.credit_note_date || - claimDetails.creditNoteDate || claimDetails.credit_note_date, - creditNoteAmount: creditNote.creditNoteAmount ? Number(creditNote.creditNoteAmount) : - (creditNote.credit_note_amount ? Number(creditNote.credit_note_amount) : - (creditNote.creditNoteAmount ? Number(creditNote.creditNoteAmount) : - (claimDetails.creditNoteAmount ? Number(claimDetails.creditNoteAmount) : - (claimDetails.credit_note_amount ? Number(claimDetails.credit_note_amount) : undefined)))), + claimDetailsObj.creditNoteDate || claimDetailsObj.credit_note_date, + creditNoteAmount: + creditNote.creditNoteAmount !== undefined && creditNote.creditNoteAmount !== null + ? Number(creditNote.creditNoteAmount) + : (creditNote.credit_note_amount !== undefined && creditNote.credit_note_amount !== null + ? Number(creditNote.credit_note_amount) + : (claimDetailsObj.creditNoteAmount !== undefined && claimDetailsObj.creditNoteAmount !== null + ? Number(claimDetailsObj.creditNoteAmount) + : (claimDetailsObj.credit_note_amount !== undefined && claimDetailsObj.credit_note_amount !== null + ? Number(claimDetailsObj.credit_note_amount) + : undefined))), // PWC fields - irn: invoice.irn || claimDetails.irn, - ackNo: invoice.ackNo || claimDetails.ackNo, - ackDate: invoice.ackDate || claimDetails.ackDate, - signedInvoiceUrl: invoice.signedInvoiceUrl || claimDetails.signedInvoiceUrl, - taxBreakdown: invoice.taxBreakdown || claimDetails.taxBreakdown, - creditNoteWfmData: apiRequest.creditNoteWfmData || claimDetails.creditNoteWfmData || [], + irn: inv.irn || claimDetailsObj.irn, + ackNo: inv.ackNo || inv.ack_no || claimDetailsObj.ackNo || claimDetailsObj.ack_no, + ackDate: inv.ackDate || inv.ack_date || claimDetailsObj.ackDate || claimDetailsObj.ack_date, + signedInvoiceUrl: inv.signedInvoiceUrl || inv.signed_invoice_url || claimDetailsObj.signedInvoiceUrl || claimDetailsObj.signed_invoice_url, + taxBreakdown: inv.taxBreakdown || claimDetailsObj.taxBreakdown, + creditNoteWfmData: (apiRequest.creditNote?.items || apiRequest.creditNoteWfmData || claimDetails.creditNoteWfmData || []).map((item: any) => ({ + ...item, + DOC_NO: item.creditNoteNumber || apiRequest.creditNote?.creditNoteNumber || item.DOC_NO, + TRNS_UNIQ_NO: item.transactionNo || item.TRNS_UNIQ_NO, + MESSAGE: item.description || item.MESSAGE, + TDS_AMT: item.tdsAmount || item.TDS_AMT, + CREDITED_TOTAL_AMT: item.creditAmount || item.amount || item.CREDITED_TOTAL_AMT, + })), }; // Map claim amounts @@ -327,18 +364,43 @@ export function mapToClaimManagementRequest( */ export function determineUserRole(apiRequest: any, currentUserId: string): RequestRole { try { + const normalize = (v: any) => (v || '').toString().trim().toLowerCase(); + const participants = Array.isArray(apiRequest.participants) ? apiRequest.participants : []; + const currentParticipant = participants.find((p: any) => + (p.userId === currentUserId || p.user_id === currentUserId || p.user?.userId === currentUserId) + ); + const currentUserEmail = normalize( + currentParticipant?.userEmail || + currentParticipant?.user_email || + currentParticipant?.email || + currentParticipant?.user?.email || + (apiRequest.initiatorId === currentUserId ? apiRequest.initiator?.email : '') + ); + const dealerEmail = normalize( + apiRequest?.claimDetails?.dealerEmail || + apiRequest?.claimDetails?.dealer_email || + apiRequest?.dealerEmail || + apiRequest?.dealer?.email + ); + + // Dealer detection first: if current user's email matches dealer email, treat as DEALER. + if (dealerEmail && currentUserEmail && currentUserEmail === dealerEmail) { + return 'DEALER'; + } + // Check if user is the initiator if (apiRequest.initiatorId === currentUserId || + apiRequest.initiator_id === currentUserId || apiRequest.initiator?.userId === currentUserId || + apiRequest.initiator?.user_id === currentUserId || apiRequest.requestedBy?.userId === currentUserId) { return 'INITIATOR'; } // Check if user is a dealer (participant with DEALER type) - const participants = apiRequest.participants || []; const dealerParticipant = participants.find((p: any) => - (p.userId === currentUserId || p.user?.userId === currentUserId) && - (p.participantType === 'DEALER' || p.type === 'DEALER') + (p.userId === currentUserId || p.user_id === currentUserId || p.user?.userId === currentUserId || p.user?.user_id === currentUserId) && + ((p.participantType || p.participant_type || p.type || '').toString().toUpperCase() === 'DEALER') ); if (dealerParticipant) { return 'DEALER'; @@ -391,7 +453,7 @@ export function getRoleBasedVisibility(role: RequestRole): RoleVisibility { showDealerInfo: true, showProposalDetails: true, showIODetails: false, - showDMSDetails: false, + showDMSDetails: true, showClaimAmount: true, canEditClaimAmount: false, }; @@ -419,10 +481,10 @@ export function getRoleBasedVisibility(role: RequestRole): RoleVisibility { case 'SPECTATOR': default: return { - showDealerInfo: false, - showProposalDetails: false, + showDealerInfo: true, + showProposalDetails: true, showIODetails: false, - showDMSDetails: false, + showDMSDetails: true, showClaimAmount: false, canEditClaimAmount: false, }; diff --git a/src/utils/claimRequestUtils.ts b/src/utils/claimRequestUtils.ts index 8deb17e..495b2ba 100644 --- a/src/utils/claimRequestUtils.ts +++ b/src/utils/claimRequestUtils.ts @@ -10,18 +10,21 @@ export function isClaimManagementRequest(request: any): boolean { if (!request) return false; + // Handle case where request is the full response object { request, claimDetails, ... } + const req = request.request || request; + // New format: Check workflowType - if (request.workflowType === 'CLAIM_MANAGEMENT') { + if (req.workflowType === 'CLAIM_MANAGEMENT') { return true; } // Old format: Check templateType (for backward compatibility) - if (request.templateType === 'claim-management' || request.template === 'claim-management') { + if (req.templateType === 'claim-management' || req.template === 'claim-management') { return true; } // Check template name/code - if (request.templateName === 'Claim Management' || request.templateCode === 'CLAIM_MANAGEMENT') { + if (req.templateName === 'Claim Management' || req.templateCode === 'CLAIM_MANAGEMENT') { return true; } @@ -35,13 +38,16 @@ export function isClaimManagementRequest(request: any): boolean { export function getWorkflowType(request: any): string { if (!request) return 'NON_TEMPLATIZED'; + // Handle wrapped response + const req = request.request || request; + // New format - if (request.workflowType) { - return request.workflowType; + if (req.workflowType) { + return req.workflowType; } // Old format: Map templateType to workflowType - if (request.templateType === 'claim-management' || request.template === 'claim-management') { + if (req.templateType === 'claim-management' || req.template === 'claim-management') { return 'CLAIM_MANAGEMENT'; }