Compare commits
No commits in common. "e00f5a4d2527a77d321af0d9571fb3ebbaaa1017" and "2a5fef6b45b2425944374a42c3b20c34ab914d9b" have entirely different histories.
e00f5a4d25
...
2a5fef6b45
4
.gitignore
vendored
4
.gitignore
vendored
@ -45,7 +45,3 @@ coverage
|
|||||||
.turbo
|
.turbo
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
|
||||||
FORM16_SAP_ACTUAL_IMPLEMENTATION_ANALYSIS.txt
|
|
||||||
FORM16_SAP_FIELDS_ONLY.txt
|
|
||||||
form16_use_cases_report.txt
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@ -350,11 +351,31 @@ export function Form16AdminConfig() {
|
|||||||
<div className="flex-1 min-w-0 space-y-3">
|
<div className="flex-1 min-w-0 space-y-3">
|
||||||
<p className="font-medium">26AS data added</p>
|
<p className="font-medium">26AS data added</p>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
<span className="font-medium text-foreground">Sent to:</span> RE users who can view 26AS. <span className="font-medium text-foreground">When:</span> As soon as new 26AS data is uploaded.
|
<span className="font-medium text-foreground">Sent to:</span> RE users who can view 26AS, and separately to all dealers. <span className="font-medium text-foreground">When:</span> As soon as new 26AS data is uploaded.
|
||||||
</p>
|
|
||||||
<p className="text-xs text-muted-foreground mt-2">
|
|
||||||
Notification messages use a fixed system template and cannot be edited here.
|
|
||||||
</p>
|
</p>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs text-muted-foreground">Message to RE users</Label>
|
||||||
|
<Textarea
|
||||||
|
rows={2}
|
||||||
|
value={notification26AsDataAdded.templateRe ?? ''}
|
||||||
|
onChange={(e) => setNotification26AsDataAdded((prev) => ({ ...prev, templateRe: e.target.value }))}
|
||||||
|
className="resize-none text-sm mt-1"
|
||||||
|
placeholder="e.g. 26AS data has been added. Please review..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs text-muted-foreground">Message to dealers</Label>
|
||||||
|
<Textarea
|
||||||
|
rows={2}
|
||||||
|
value={notification26AsDataAdded.templateDealers ?? ''}
|
||||||
|
onChange={(e) => setNotification26AsDataAdded((prev) => ({ ...prev, templateDealers: e.target.value }))}
|
||||||
|
className="resize-none text-sm mt-1"
|
||||||
|
placeholder="e.g. New 26AS data has been uploaded. You can submit Form 16..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">Placeholders: [Name], [Request ID]</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
checked={notification26AsDataAdded.enabled}
|
checked={notification26AsDataAdded.enabled}
|
||||||
@ -369,9 +390,16 @@ export function Form16AdminConfig() {
|
|||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
<span className="font-medium text-foreground">Sent to:</span> The dealer who submitted the Form 16. <span className="font-medium text-foreground">When:</span> Immediately after their submission is matched with 26AS and a credit note is generated.
|
<span className="font-medium text-foreground">Sent to:</span> The dealer who submitted the Form 16. <span className="font-medium text-foreground">When:</span> Immediately after their submission is matched with 26AS and a credit note is generated.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-muted-foreground mt-2">
|
<div className="mt-2">
|
||||||
Notification message is fixed in the system configuration.
|
<Textarea
|
||||||
</p>
|
rows={2}
|
||||||
|
value={notificationForm16SuccessCreditNote.template ?? ''}
|
||||||
|
onChange={(e) => setNotificationForm16SuccessCreditNote((prev) => ({ ...prev, template: e.target.value }))}
|
||||||
|
className="resize-none text-sm"
|
||||||
|
placeholder="Message template..."
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">Placeholders: [Name], [CreditNoteRef], [Request ID]</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
checked={notificationForm16SuccessCreditNote.enabled}
|
checked={notificationForm16SuccessCreditNote.enabled}
|
||||||
@ -386,9 +414,16 @@ export function Form16AdminConfig() {
|
|||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
<span className="font-medium text-foreground">Sent to:</span> The dealer who submitted. <span className="font-medium text-foreground">When:</span> When their submission fails (e.g. value mismatch with 26AS, duplicate, or validation error) so they can correct and resubmit.
|
<span className="font-medium text-foreground">Sent to:</span> The dealer who submitted. <span className="font-medium text-foreground">When:</span> When their submission fails (e.g. value mismatch with 26AS, duplicate, or validation error) so they can correct and resubmit.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-muted-foreground mt-2">
|
<div className="mt-2">
|
||||||
Notification message is fixed in the system configuration.
|
<Textarea
|
||||||
</p>
|
rows={2}
|
||||||
|
value={notificationForm16Unsuccessful.template ?? ''}
|
||||||
|
onChange={(e) => setNotificationForm16Unsuccessful((prev) => ({ ...prev, template: e.target.value }))}
|
||||||
|
className="resize-none text-sm"
|
||||||
|
placeholder="Message template..."
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">Placeholders: [Name], [Issue], [Request ID]</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
checked={notificationForm16Unsuccessful.enabled}
|
checked={notificationForm16Unsuccessful.enabled}
|
||||||
@ -448,9 +483,16 @@ export function Form16AdminConfig() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground">
|
<div>
|
||||||
Alert message content is fixed and managed by the system.
|
<Textarea
|
||||||
</p>
|
rows={2}
|
||||||
|
value={alertSubmitForm16Template}
|
||||||
|
onChange={(e) => setAlertSubmitForm16Template(e.target.value)}
|
||||||
|
className="resize-none text-sm mt-1"
|
||||||
|
placeholder="Message template for alert to dealers..."
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">Placeholders: [Name], [DueDate], [Request ID]</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
@ -511,9 +553,16 @@ export function Form16AdminConfig() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground">
|
<div>
|
||||||
Reminder message content is fixed and managed by the system.
|
<Textarea
|
||||||
</p>
|
rows={2}
|
||||||
|
value={reminderNotificationTemplate}
|
||||||
|
onChange={(e) => setReminderNotificationTemplate(e.target.value)}
|
||||||
|
className="resize-none text-sm mt-1"
|
||||||
|
placeholder="Message template for reminder to dealers..."
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">Placeholders: [Name], [Request ID], [Status], [TAT]</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
|
|||||||
@ -7,11 +7,22 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { Label } from '@/components/ui/label';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
import { Loader2, Receipt, X, RotateCcw } from 'lucide-react';
|
import { Loader2, Receipt, X, RotateCcw } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
getCreditNoteByRequestId,
|
getCreditNoteByRequestId,
|
||||||
cancelForm16Submission,
|
cancelForm16Submission,
|
||||||
setForm16ResubmissionNeeded,
|
setForm16ResubmissionNeeded,
|
||||||
|
generateForm16CreditNoteManually,
|
||||||
} from '@/services/form16Api';
|
} from '@/services/form16Api';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
@ -24,11 +35,14 @@ interface Form16QuickActionsProps {
|
|||||||
export function Form16QuickActions({ requestId, request, onRefresh }: Form16QuickActionsProps) {
|
export function Form16QuickActions({ requestId, request, onRefresh }: Form16QuickActionsProps) {
|
||||||
const [creditNote, setCreditNote] = useState<{ id: number; status: string } | null>(null);
|
const [creditNote, setCreditNote] = useState<{ id: number; status: string } | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [actionLoading, setActionLoading] = useState<'cancel' | 'resubmit' | null>(null);
|
const [actionLoading, setActionLoading] = useState<'cancel' | 'resubmit' | 'credit' | null>(null);
|
||||||
|
const [generateCnOpen, setGenerateCnOpen] = useState(false);
|
||||||
|
const [generateCnAmount, setGenerateCnAmount] = useState('');
|
||||||
|
|
||||||
const form16 = request?.form16Submission;
|
const form16 = request?.form16Submission;
|
||||||
const hasSubmission = !!form16;
|
const hasSubmission = !!form16;
|
||||||
const hasCreditNote = !!creditNote && creditNote.status !== 'withdrawn';
|
const hasCreditNote = !!creditNote && creditNote.status !== 'withdrawn';
|
||||||
|
const suggestedAmount = form16?.tdsAmount != null ? Number(form16.tdsAmount) : undefined;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!requestId) {
|
if (!requestId) {
|
||||||
@ -77,9 +91,32 @@ export function Form16QuickActions({ requestId, request, onRefresh }: Form16Quic
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleGenerateCreditNote = async () => {
|
||||||
|
const amount = parseFloat(generateCnAmount);
|
||||||
|
if (!requestId || Number.isNaN(amount) || amount <= 0) {
|
||||||
|
toast.error('Enter a valid amount to generate credit note');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setActionLoading('credit');
|
||||||
|
try {
|
||||||
|
await generateForm16CreditNoteManually(requestId, amount);
|
||||||
|
setGenerateCnOpen(false);
|
||||||
|
setGenerateCnAmount('');
|
||||||
|
toast.success('Credit note generated (manually approved)');
|
||||||
|
const note = await getCreditNoteByRequestId(requestId);
|
||||||
|
setCreditNote(note ? { id: typeof note.id === 'number' ? note.id : Number(note.id), status: note.status || '' } : null);
|
||||||
|
onRefresh?.();
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(e instanceof Error ? e.message : 'Failed to generate credit note');
|
||||||
|
} finally {
|
||||||
|
setActionLoading(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (loading || !hasSubmission || hasCreditNote) return null;
|
if (loading || !hasSubmission || hasCreditNote) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Card className="border-blue-200 bg-blue-50/30" data-testid="form16-quick-actions-card">
|
<Card className="border-blue-200 bg-blue-50/30" data-testid="form16-quick-actions-card">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
<CardTitle className="text-sm flex items-center gap-2 text-blue-800">
|
<CardTitle className="text-sm flex items-center gap-2 text-blue-800">
|
||||||
@ -87,7 +124,7 @@ export function Form16QuickActions({ requestId, request, onRefresh }: Form16Quic
|
|||||||
Form 16 actions
|
Form 16 actions
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription className="text-xs text-gray-600">
|
<CardDescription className="text-xs text-gray-600">
|
||||||
View the document in the Documents tab. Cancel submission or mark resubmission needed.
|
View the document in the Documents tab. Cancel submission, mark resubmission needed, or generate credit note (e.g. when OCR was partial).
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-2">
|
<CardContent className="space-y-2">
|
||||||
@ -111,7 +148,58 @@ export function Form16QuickActions({ requestId, request, onRefresh }: Form16Quic
|
|||||||
{actionLoading === 'resubmit' ? <Loader2 className="w-3 h-3 animate-spin mr-1" /> : <RotateCcw className="w-3 h-3 mr-1" />}
|
{actionLoading === 'resubmit' ? <Loader2 className="w-3 h-3 animate-spin mr-1" /> : <RotateCcw className="w-3 h-3 mr-1" />}
|
||||||
Resubmission needed
|
Resubmission needed
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="w-full justify-start border-emerald-300 text-emerald-700 hover:bg-emerald-50"
|
||||||
|
onClick={() => {
|
||||||
|
setGenerateCnAmount(suggestedAmount != null ? String(suggestedAmount) : '');
|
||||||
|
setGenerateCnOpen(true);
|
||||||
|
}}
|
||||||
|
disabled={!!actionLoading}
|
||||||
|
>
|
||||||
|
{actionLoading === 'credit' ? <Loader2 className="w-3 h-3 animate-spin mr-1" /> : <Receipt className="w-3 h-3 mr-1" />}
|
||||||
|
Generate credit note
|
||||||
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<Dialog open={generateCnOpen} onOpenChange={setGenerateCnOpen}>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Generate credit note (manual)</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Enter the amount for the credit note. This will mark the Form 16 as manually approved.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4 py-4">
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor="form16-cn-amount">Amount (₹)</Label>
|
||||||
|
<Input
|
||||||
|
id="form16-cn-amount"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
placeholder={suggestedAmount != null ? String(suggestedAmount) : '0'}
|
||||||
|
value={generateCnAmount}
|
||||||
|
onChange={(e) => setGenerateCnAmount(e.target.value)}
|
||||||
|
/>
|
||||||
|
{suggestedAmount != null && (
|
||||||
|
<p className="text-xs text-gray-500">Suggested from submission TDS amount: ₹{suggestedAmount.toLocaleString('en-IN')}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={() => setGenerateCnOpen(false)} disabled={!!actionLoading}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleGenerateCreditNote} disabled={!!actionLoading || !generateCnAmount.trim()}>
|
||||||
|
{actionLoading === 'credit' ? <Loader2 className="w-4 h-4 animate-spin mr-2" /> : null}
|
||||||
|
Generate credit note
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,6 @@ interface DMSDetails {
|
|||||||
ackNo?: string;
|
ackNo?: string;
|
||||||
ackDate?: string;
|
ackDate?: string;
|
||||||
signedInvoiceUrl?: string;
|
signedInvoiceUrl?: string;
|
||||||
creditNoteWfmData?: any[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClaimAmountDetails {
|
interface ClaimAmountDetails {
|
||||||
@ -100,7 +99,6 @@ export function ProcessDetailsCard({
|
|||||||
const hasContent =
|
const hasContent =
|
||||||
(visibility.showIODetails && ioDetails) ||
|
(visibility.showIODetails && ioDetails) ||
|
||||||
(visibility.showDMSDetails && dmsDetails) ||
|
(visibility.showDMSDetails && dmsDetails) ||
|
||||||
(dmsDetails?.creditNoteWfmData && dmsDetails.creditNoteWfmData.length > 0) ||
|
|
||||||
(visibility.showClaimAmount && claimAmount && claimAmount.amount !== undefined && claimAmount.amount !== null) ||
|
(visibility.showClaimAmount && claimAmount && claimAmount.amount !== undefined && claimAmount.amount !== null) ||
|
||||||
(estimatedBudgetBreakdown && estimatedBudgetBreakdown.length > 0) ||
|
(estimatedBudgetBreakdown && estimatedBudgetBreakdown.length > 0) ||
|
||||||
(closedExpensesBreakdown && closedExpensesBreakdown.length > 0);
|
(closedExpensesBreakdown && closedExpensesBreakdown.length > 0);
|
||||||
@ -229,32 +227,6 @@ export function ProcessDetailsCard({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Credit Note Validation */}
|
|
||||||
{dmsDetails?.creditNoteWfmData && dmsDetails.creditNoteWfmData.length > 0 && (
|
|
||||||
<div className="bg-white rounded-lg p-3 border border-pink-200">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<Receipt className="w-4 h-4 text-pink-600" />
|
|
||||||
<Label className="text-xs font-semibold text-pink-900 uppercase tracking-wide">
|
|
||||||
Credit Note Validation
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{dmsDetails.creditNoteWfmData.map((cn: any, idx: number) => (
|
|
||||||
<div key={idx} className="bg-pink-50 p-2 rounded border border-pink-100 text-[10px]">
|
|
||||||
<div className="flex justify-between mb-1 text-pink-900">
|
|
||||||
<span className="font-semibold break-words flex-1 pr-2">Credit Note No: {cn.DOC_NO || 'N/A'}</span>
|
|
||||||
<span className={`px-1.5 py-0.5 rounded-sm text-white font-medium self-start whitespace-nowrap ${cn.MSG_TYP === 'E' ? 'bg-red-500' : 'bg-green-500'}`}>
|
|
||||||
{cn.MSG_TYP === 'E' ? 'Error' : (cn.MSG_TYP === 'S' ? 'Success' : cn.MSG_TYP || 'Unknown')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="text-gray-500 mb-0.5">Txn: {cn.TRNS_UNIQ_NO || 'N/A'}</div>
|
|
||||||
<div className="text-gray-700 break-words font-medium">{cn.MESSAGE || 'No Message'}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Claim Amount */}
|
{/* Claim Amount */}
|
||||||
{visibility.showClaimAmount && claimAmount && (
|
{visibility.showClaimAmount && claimAmount && (
|
||||||
<div className="bg-white rounded-lg p-3 border border-green-200">
|
<div className="bg-white rounded-lg p-3 border border-green-200">
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { useState, useEffect, useMemo, useCallback } from 'react';
|
|||||||
import workflowApi, { getPauseDetails } from '@/services/workflowApi';
|
import workflowApi, { getPauseDetails } from '@/services/workflowApi';
|
||||||
import apiClient from '@/services/authApi';
|
import apiClient from '@/services/authApi';
|
||||||
import { getSocket } from '@/utils/socket';
|
import { getSocket } from '@/utils/socket';
|
||||||
import { isClaimManagementRequest } from '@/utils/claimRequestUtils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom Hook: useRequestDetails
|
* Custom Hook: useRequestDetails
|
||||||
@ -240,18 +239,8 @@ export function useRequestDetails(
|
|||||||
let completionDetails = null;
|
let completionDetails = null;
|
||||||
let internalOrder = null;
|
let internalOrder = null;
|
||||||
let internalOrders = [];
|
let internalOrders = [];
|
||||||
let creditNoteWfmData = null;
|
|
||||||
|
|
||||||
if (isClaimManagementRequest(wf)) {
|
|
||||||
// Fetch Credit Note independently
|
|
||||||
try {
|
|
||||||
const cnResponse = await apiClient.get(`/dealer-claims/${wf.requestId}/credit-note-wfm`);
|
|
||||||
const responseData = cnResponse.data ?? cnResponse;
|
|
||||||
creditNoteWfmData = Array.isArray(responseData?.data) ? responseData.data : (Array.isArray(responseData) ? responseData : []);
|
|
||||||
} catch (cnError) {
|
|
||||||
console.warn('[useRequestDetails] Error fetching credit note WFM data:', cnError);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (wf.workflowType === 'CLAIM_MANAGEMENT' || wf.templateType === 'claim-management') {
|
||||||
try {
|
try {
|
||||||
const claimResponse = await apiClient.get(`/dealer-claims/${wf.requestId}`);
|
const claimResponse = await apiClient.get(`/dealer-claims/${wf.requestId}`);
|
||||||
|
|
||||||
@ -275,7 +264,6 @@ export function useRequestDetails(
|
|||||||
(claimDetails as any).invoice = invoice;
|
(claimDetails as any).invoice = invoice;
|
||||||
(claimDetails as any).creditNote = creditNote;
|
(claimDetails as any).creditNote = creditNote;
|
||||||
(claimDetails as any).completionExpenses = completionExpenses;
|
(claimDetails as any).completionExpenses = completionExpenses;
|
||||||
(claimDetails as any).creditNoteWfmData = creditNoteWfmData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extracted details processed
|
// Extracted details processed
|
||||||
@ -345,7 +333,6 @@ export function useRequestDetails(
|
|||||||
budgetTracking: (claimDetails as any)?.budgetTracking || null,
|
budgetTracking: (claimDetails as any)?.budgetTracking || null,
|
||||||
invoice: claimDetails?.invoice || (claimDetails as any)?.invoice || null,
|
invoice: claimDetails?.invoice || (claimDetails as any)?.invoice || null,
|
||||||
creditNote: (claimDetails as any)?.creditNote || null,
|
creditNote: (claimDetails as any)?.creditNote || null,
|
||||||
creditNoteWfmData: creditNoteWfmData || (claimDetails as any)?.creditNoteWfmData || null,
|
|
||||||
completionExpenses: (claimDetails as any)?.completionExpenses || null,
|
completionExpenses: (claimDetails as any)?.completionExpenses || null,
|
||||||
templateType: wf.templateType || wf.template_type,
|
templateType: wf.templateType || wf.template_type,
|
||||||
form16Submission: details.form16Submission || null,
|
form16Submission: details.form16Submission || null,
|
||||||
@ -536,18 +523,8 @@ export function useRequestDetails(
|
|||||||
let completionDetails = null;
|
let completionDetails = null;
|
||||||
let internalOrder = null;
|
let internalOrder = null;
|
||||||
let internalOrders = [];
|
let internalOrders = [];
|
||||||
let creditNoteWfmData = null;
|
|
||||||
|
|
||||||
if (isClaimManagementRequest(wf)) {
|
|
||||||
// Fetch Credit Note independently
|
|
||||||
try {
|
|
||||||
const cnResponse = await apiClient.get(`/dealer-claims/${wf.requestId}/credit-note-wfm`);
|
|
||||||
const responseData = cnResponse.data ?? cnResponse;
|
|
||||||
creditNoteWfmData = Array.isArray(responseData?.data) ? responseData.data : (Array.isArray(responseData) ? responseData : []);
|
|
||||||
} catch (cnError) {
|
|
||||||
console.warn('[useRequestDetails] Error fetching credit note WFM data:', cnError);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (wf.workflowType === 'CLAIM_MANAGEMENT' || wf.templateType === 'claim-management') {
|
||||||
try {
|
try {
|
||||||
const claimResponse = await apiClient.get(`/dealer-claims/${wf.requestId}`);
|
const claimResponse = await apiClient.get(`/dealer-claims/${wf.requestId}`);
|
||||||
|
|
||||||
@ -570,7 +547,6 @@ export function useRequestDetails(
|
|||||||
(claimDetails as any).invoice = invoice;
|
(claimDetails as any).invoice = invoice;
|
||||||
(claimDetails as any).creditNote = creditNote;
|
(claimDetails as any).creditNote = creditNote;
|
||||||
(claimDetails as any).completionExpenses = completionExpenses;
|
(claimDetails as any).completionExpenses = completionExpenses;
|
||||||
(claimDetails as any).creditNoteWfmData = creditNoteWfmData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial load - Extracted details processed
|
// Initial load - Extracted details processed
|
||||||
@ -627,7 +603,6 @@ export function useRequestDetails(
|
|||||||
budgetTracking: (claimDetails as any)?.budgetTracking || null,
|
budgetTracking: (claimDetails as any)?.budgetTracking || null,
|
||||||
invoice: (claimDetails as any)?.invoice || null,
|
invoice: (claimDetails as any)?.invoice || null,
|
||||||
creditNote: (claimDetails as any)?.creditNote || null,
|
creditNote: (claimDetails as any)?.creditNote || null,
|
||||||
creditNoteWfmData: creditNoteWfmData || (claimDetails as any)?.creditNoteWfmData || null,
|
|
||||||
completionExpenses: (claimDetails as any)?.completionExpenses || null,
|
completionExpenses: (claimDetails as any)?.completionExpenses || null,
|
||||||
templateType: wf.templateType || wf.template_type,
|
templateType: wf.templateType || wf.template_type,
|
||||||
form16Submission: details.form16Submission || null,
|
form16Submission: details.form16Submission || null,
|
||||||
|
|||||||
@ -132,6 +132,18 @@ export async function setForm16ResubmissionNeeded(requestId: string): Promise<vo
|
|||||||
await apiClient.post(`/form16/requests/${encodeURIComponent(requestId)}/resubmission-needed`);
|
await apiClient.post(`/form16/requests/${encodeURIComponent(requestId)}/resubmission-needed`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** RE only. Manually generate credit note for Form 16 request. Body: { amount: number }. */
|
||||||
|
export async function generateForm16CreditNoteManually(requestId: string, amount: number): Promise<{ creditNote: Form16CreditNoteItem }> {
|
||||||
|
const { data } = await apiClient.post<{ data?: { creditNote?: Form16CreditNoteItem }; creditNote?: Form16CreditNoteItem }>(
|
||||||
|
`/form16/requests/${encodeURIComponent(requestId)}/generate-credit-note`,
|
||||||
|
{ amount }
|
||||||
|
);
|
||||||
|
const payload = data?.data ?? data;
|
||||||
|
const creditNote = payload?.creditNote;
|
||||||
|
if (!creditNote) throw new Error('Credit note not returned');
|
||||||
|
return { creditNote };
|
||||||
|
}
|
||||||
|
|
||||||
/** Get a single credit note by id with dealer info and dealer transaction history. */
|
/** Get a single credit note by id with dealer info and dealer transaction history. */
|
||||||
export interface CreditNoteDetailResponse {
|
export interface CreditNoteDetailResponse {
|
||||||
creditNote: Form16CreditNoteItem & { submission?: { submittedDate?: string | null } };
|
creditNote: Form16CreditNoteItem & { submission?: { submittedDate?: string | null } };
|
||||||
|
|||||||
@ -95,7 +95,6 @@ export interface ClaimManagementRequest {
|
|||||||
ackDate?: string;
|
ackDate?: string;
|
||||||
signedInvoiceUrl?: string;
|
signedInvoiceUrl?: string;
|
||||||
taxBreakdown?: any;
|
taxBreakdown?: any;
|
||||||
creditNoteWfmData?: any[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Claim Amount
|
// Claim Amount
|
||||||
@ -299,7 +298,6 @@ export function mapToClaimManagementRequest(
|
|||||||
ackDate: invoice.ackDate || claimDetails.ackDate,
|
ackDate: invoice.ackDate || claimDetails.ackDate,
|
||||||
signedInvoiceUrl: invoice.signedInvoiceUrl || claimDetails.signedInvoiceUrl,
|
signedInvoiceUrl: invoice.signedInvoiceUrl || claimDetails.signedInvoiceUrl,
|
||||||
taxBreakdown: invoice.taxBreakdown || claimDetails.taxBreakdown,
|
taxBreakdown: invoice.taxBreakdown || claimDetails.taxBreakdown,
|
||||||
creditNoteWfmData: apiRequest.creditNoteWfmData || claimDetails.creditNoteWfmData || [],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map claim amounts
|
// Map claim amounts
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user