Fixed claim number issue at debit note and SAP responses from database
This commit is contained in:
parent
4b725e42ca
commit
3a6a8c2472
@ -132,12 +132,12 @@ export function Form16AdminConfig() {
|
||||
const [alertSubmitForm16FrequencyDays, setAlertSubmitForm16FrequencyDays] = useState(0);
|
||||
const [alertSubmitForm16FrequencyHours, setAlertSubmitForm16FrequencyHours] = useState(24);
|
||||
const [alertSubmitForm16RunAtTime, setAlertSubmitForm16RunAtTime] = useState('09:00');
|
||||
const [alertSubmitForm16Template, setAlertSubmitForm16Template] = useState('Please submit your Form 16 at your earliest. [Name], due date: [DueDate].');
|
||||
const [alertSubmitForm16Template, setAlertSubmitForm16Template] = useState('Dear [Name], please submit Form 16A for the pending period. Due: [DueDate].');
|
||||
const [reminderNotificationEnabled, setReminderNotificationEnabled] = useState(true);
|
||||
const [reminderFrequencyDays, setReminderFrequencyDays] = useState(0);
|
||||
const [reminderFrequencyHours, setReminderFrequencyHours] = useState(12);
|
||||
const [reminderRunAtTime, setReminderRunAtTime] = useState('10:00');
|
||||
const [reminderNotificationTemplate, setReminderNotificationTemplate] = useState('Reminder: Form 16 submission is pending. [Name], [Request ID]. Please review.');
|
||||
const [reminderNotificationTemplate, setReminderNotificationTemplate] = useState('Reminder: Dear [Name], your Form 16A submission is pending for request [Request ID]. Please complete it.');
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
@ -166,12 +166,12 @@ export function Form16AdminConfig() {
|
||||
setAlertSubmitForm16FrequencyDays(config.alertSubmitForm16FrequencyDays ?? 0);
|
||||
setAlertSubmitForm16FrequencyHours(config.alertSubmitForm16FrequencyHours ?? 24);
|
||||
setAlertSubmitForm16RunAtTime(config.alertSubmitForm16RunAtTime !== undefined && config.alertSubmitForm16RunAtTime !== null ? config.alertSubmitForm16RunAtTime : '09:00');
|
||||
setAlertSubmitForm16Template(config.alertSubmitForm16Template ?? 'Please submit your Form 16 at your earliest. [Name], due date: [DueDate].');
|
||||
setAlertSubmitForm16Template(config.alertSubmitForm16Template ?? 'Dear [Name], please submit Form 16A for the pending period. Due: [DueDate].');
|
||||
setReminderNotificationEnabled(config.reminderNotificationEnabled ?? true);
|
||||
setReminderFrequencyDays(config.reminderFrequencyDays ?? 0);
|
||||
setReminderFrequencyHours(config.reminderFrequencyHours ?? 12);
|
||||
setReminderRunAtTime(config.reminderRunAtTime !== undefined && config.reminderRunAtTime !== null ? config.reminderRunAtTime : '10:00');
|
||||
setReminderNotificationTemplate(config.reminderNotificationTemplate ?? 'Reminder: Form 16 submission is pending. [Name], [Request ID]. Please review.');
|
||||
setReminderNotificationTemplate(config.reminderNotificationTemplate ?? 'Reminder: Dear [Name], your Form 16A submission is pending for request [Request ID]. Please complete it.');
|
||||
})
|
||||
.catch(() => {
|
||||
if (mounted) toast.error('Failed to load Form 16 configuration');
|
||||
|
||||
@ -6,9 +6,8 @@ import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { ArrowLeft, Mail, Download, Loader2, RefreshCw, FileText, IndianRupee, CalendarClock } from 'lucide-react';
|
||||
import { getCreditNoteById, getCreditNoteDownloadUrl, type CreditNoteDetailResponse } from '@/services/form16Api';
|
||||
import { getCreditNoteById, getCreditNoteSapResponse, type CreditNoteDetailResponse, type Form16SapResponseRecord } from '@/services/form16Api';
|
||||
import { toast } from 'sonner';
|
||||
import apiClient from '@/services/authApi';
|
||||
|
||||
function formatDate(value: string | null | undefined): string {
|
||||
if (!value) return '–';
|
||||
@ -25,41 +24,14 @@ function formatAmount(value: number | null | undefined): string {
|
||||
return new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR', maximumFractionDigits: 0 }).format(value);
|
||||
}
|
||||
|
||||
function buildAbsoluteBackendUrl(url: string): string {
|
||||
if (!url) return url;
|
||||
if (/^https?:\/\//i.test(url)) return url;
|
||||
const apiBase = ((import.meta as any).env?.VITE_API_BASE_URL as string | undefined) || 'http://localhost:5000/api/v1';
|
||||
const origin = apiBase.replace(/\/api\/v1\/?$/i, '');
|
||||
return `${origin}${url.startsWith('/') ? '' : '/'}${url}`;
|
||||
}
|
||||
|
||||
function parseSapResponseCsv(rawCsv: string): Record<string, string> | null {
|
||||
const lines = rawCsv
|
||||
.split(/\r?\n/)
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean);
|
||||
if (lines.length < 2) return null;
|
||||
const header = lines[0]!.split('|').map((h) => h.trim());
|
||||
const isUsefulRow = (values: string[]) => {
|
||||
const obj: Record<string, string> = {};
|
||||
header.forEach((h, idx) => (obj[h] = (values[idx] || '').trim()));
|
||||
const trns = (obj.TRNS_UNIQ_NO || '').trim();
|
||||
const docNo = (obj.DOC_NO || '').trim();
|
||||
const msgTyp = (obj.MSG_TYP || '').trim();
|
||||
const tdsId = (obj.TDS_TRNS_ID || '').trim();
|
||||
if (trns) return true;
|
||||
if (tdsId && (docNo || msgTyp) && tdsId.toUpperCase() !== 'MSG_TYP' && tdsId.toUpperCase() !== 'MESSAGE') return true;
|
||||
return false;
|
||||
function mapSapResponseToPreviewRow(sap: Form16SapResponseRecord): Record<string, string> {
|
||||
return {
|
||||
TRNS_UNIQ_NO: sap.trnsUniqNo || '',
|
||||
TDS_TRNS_ID: sap.tdsTransId || sap.claimNumber || '',
|
||||
DOC_NO: sap.sapDocumentNumber || '',
|
||||
MSG_TYP: sap.msgTyp || '',
|
||||
MESSAGE: sap.message || '',
|
||||
};
|
||||
for (let i = lines.length - 1; i >= 1; i--) {
|
||||
const values = lines[i]!.split('|');
|
||||
if (isUsefulRow(values)) {
|
||||
const obj: Record<string, string> = {};
|
||||
header.forEach((h, idx) => (obj[h] = (values[idx] || '').trim()));
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function formatIssuedAt(value: string | null | undefined): string {
|
||||
@ -83,7 +55,7 @@ export function Form16CreditNoteDetail() {
|
||||
const [previewOpen, setPreviewOpen] = useState(false);
|
||||
const [previewLoading, setPreviewLoading] = useState(false);
|
||||
const [previewRow, setPreviewRow] = useState<Record<string, string> | null>(null);
|
||||
const [previewCsv, setPreviewCsv] = useState<string>('');
|
||||
const [previewDownloadUrl, setPreviewDownloadUrl] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const numId = id ? parseInt(id, 10) : NaN;
|
||||
@ -134,18 +106,14 @@ export function Form16CreditNoteDetail() {
|
||||
const numId = id ? parseInt(id, 10) : NaN;
|
||||
if (Number.isNaN(numId)) return;
|
||||
try {
|
||||
const url = await getCreditNoteDownloadUrl(numId);
|
||||
setPreviewOpen(true);
|
||||
setPreviewLoading(true);
|
||||
setPreviewRow(null);
|
||||
setPreviewCsv('');
|
||||
const absUrl = buildAbsoluteBackendUrl(url);
|
||||
const res = await apiClient.get<Blob>(absUrl, { responseType: 'blob' });
|
||||
const rawCsv = await res.data.text();
|
||||
const row = parseSapResponseCsv(rawCsv);
|
||||
if (!row) throw new Error('Could not parse SAP response CSV');
|
||||
setPreviewDownloadUrl(null);
|
||||
const payload = await getCreditNoteSapResponse(numId);
|
||||
const row = mapSapResponseToPreviewRow(payload.sapResponse);
|
||||
setPreviewRow(row);
|
||||
setPreviewCsv(rawCsv);
|
||||
setPreviewDownloadUrl(payload.url || null);
|
||||
} catch (e: any) {
|
||||
const msg =
|
||||
e?.response?.status === 409
|
||||
@ -184,7 +152,7 @@ export function Form16CreditNoteDetail() {
|
||||
setPreviewOpen(open);
|
||||
if (!open) {
|
||||
setPreviewRow(null);
|
||||
setPreviewCsv('');
|
||||
setPreviewDownloadUrl(null);
|
||||
}
|
||||
}}
|
||||
>
|
||||
@ -268,19 +236,13 @@ export function Form16CreditNoteDetail() {
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (!previewCsv) return;
|
||||
const fileName = `${creditNote.creditNoteNumber || `credit-note-${id}`}.csv`;
|
||||
const blob = new Blob([previewCsv], { type: 'text/csv;charset=utf-8' });
|
||||
const href = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = href;
|
||||
a.download = fileName;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(href);
|
||||
if (!previewDownloadUrl) {
|
||||
toast.error('CSV download link not available');
|
||||
return;
|
||||
}
|
||||
window.open(previewDownloadUrl, '_blank');
|
||||
}}
|
||||
disabled={!previewRow || previewLoading}
|
||||
disabled={!previewRow || previewLoading || !previewDownloadUrl}
|
||||
>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Download CSV
|
||||
|
||||
@ -8,8 +8,9 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, D
|
||||
import { Search, Loader2, Receipt, Download, RefreshCw, FileText, IndianRupee, CalendarClock } from 'lucide-react';
|
||||
import {
|
||||
listCreditNotes,
|
||||
getCreditNoteDownloadUrl,
|
||||
getCreditNoteSapResponse,
|
||||
type Form16CreditNoteItem,
|
||||
type Form16SapResponseRecord,
|
||||
type ListCreditNotesParams,
|
||||
type ListCreditNotesSummary,
|
||||
} from '@/services/form16Api';
|
||||
@ -38,9 +39,8 @@ const DEFAULT_SUMMARY: ListCreditNotesSummary = {
|
||||
};
|
||||
|
||||
type SapResponsePreview = {
|
||||
fileUrl: string;
|
||||
fileName: string;
|
||||
rawCsv: string;
|
||||
downloadUrl?: string | null;
|
||||
row: Record<string, string>;
|
||||
meta: {
|
||||
amountText: string;
|
||||
@ -48,43 +48,14 @@ type SapResponsePreview = {
|
||||
};
|
||||
};
|
||||
|
||||
function buildAbsoluteBackendUrl(url: string): string {
|
||||
if (!url) return url;
|
||||
if (/^https?:\/\//i.test(url)) return url;
|
||||
const apiBase = ((import.meta as any).env?.VITE_API_BASE_URL as string | undefined) || 'http://localhost:5000/api/v1';
|
||||
const origin = apiBase.replace(/\/api\/v1\/?$/i, '');
|
||||
return `${origin}${url.startsWith('/') ? '' : '/'}${url}`;
|
||||
}
|
||||
|
||||
function parseSapResponseCsv(rawCsv: string): Record<string, string> | null {
|
||||
const lines = rawCsv
|
||||
.split(/\r?\n/)
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean);
|
||||
if (lines.length < 2) return null;
|
||||
|
||||
const header = lines[0]!.split('|').map((h) => h.trim());
|
||||
const isUsefulRow = (values: string[]) => {
|
||||
const obj: Record<string, string> = {};
|
||||
header.forEach((h, idx) => (obj[h] = (values[idx] || '').trim()));
|
||||
const trns = (obj.TRNS_UNIQ_NO || '').trim();
|
||||
const docNo = (obj.DOC_NO || '').trim();
|
||||
const msgTyp = (obj.MSG_TYP || '').trim();
|
||||
const tdsId = (obj.TDS_TRNS_ID || '').trim();
|
||||
if (trns) return true;
|
||||
if (tdsId && (docNo || msgTyp) && tdsId.toUpperCase() !== 'MSG_TYP' && tdsId.toUpperCase() !== 'MESSAGE') return true;
|
||||
return false;
|
||||
function mapSapResponseToPreviewRow(sap: Form16SapResponseRecord): Record<string, string> {
|
||||
return {
|
||||
TRNS_UNIQ_NO: sap.trnsUniqNo || '',
|
||||
TDS_TRNS_ID: sap.tdsTransId || sap.claimNumber || '',
|
||||
DOC_NO: sap.sapDocumentNumber || '',
|
||||
MSG_TYP: sap.msgTyp || '',
|
||||
MESSAGE: sap.message || '',
|
||||
};
|
||||
|
||||
for (let i = lines.length - 1; i >= 1; i--) {
|
||||
const values = lines[i]!.split('|');
|
||||
if (isUsefulRow(values)) {
|
||||
const obj: Record<string, string> = {};
|
||||
header.forEach((h, idx) => (obj[h] = (values[idx] || '').trim()));
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function formatIssuedAt(value: string | null | undefined): string {
|
||||
@ -133,22 +104,16 @@ export function Form16CreditNotes() {
|
||||
setPreviewLoading(true);
|
||||
setPreview(null);
|
||||
try {
|
||||
const url = await getCreditNoteDownloadUrl(noteId);
|
||||
const absUrl = buildAbsoluteBackendUrl(url);
|
||||
const payload = await getCreditNoteSapResponse(noteId);
|
||||
const fileName = (creditNoteNumber || `credit-note-${noteId}`).trim() + '.csv';
|
||||
|
||||
const res = await apiClient.get<Blob>(absUrl, { responseType: 'blob' });
|
||||
const blob = res.data;
|
||||
const rawCsv = await blob.text();
|
||||
const row = parseSapResponseCsv(rawCsv);
|
||||
if (!row) throw new Error('Could not parse SAP response CSV');
|
||||
const row = mapSapResponseToPreviewRow(payload.sapResponse);
|
||||
|
||||
// Enrich with UI meta (amount/date from listing row when available)
|
||||
const note = creditNotes.find((n) => Number(n.id) === Number(noteId));
|
||||
const amountText = formatAmount(note?.amount ?? null);
|
||||
const issuedAtText = formatIssuedAt(note?.issueDate ?? null);
|
||||
|
||||
setPreview({ fileUrl: absUrl, fileName, rawCsv, row, meta: { amountText, issuedAtText } });
|
||||
setPreview({ fileName, row, meta: { amountText, issuedAtText }, downloadUrl: payload.url || null });
|
||||
} catch (e: any) {
|
||||
const msg =
|
||||
e?.response?.status === 409
|
||||
@ -162,16 +127,11 @@ export function Form16CreditNotes() {
|
||||
}, [creditNotes]);
|
||||
|
||||
const downloadPreviewCsv = useCallback(async () => {
|
||||
if (!preview) return;
|
||||
const blob = new Blob([preview.rawCsv], { type: 'text/csv;charset=utf-8' });
|
||||
const href = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = href;
|
||||
a.download = preview.fileName;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(href);
|
||||
if (!preview?.downloadUrl) {
|
||||
toast.error('CSV download link not available');
|
||||
return;
|
||||
}
|
||||
window.open(preview.downloadUrl, '_blank');
|
||||
}, [preview]);
|
||||
|
||||
const filteredNotes = useMemo(() => {
|
||||
@ -273,7 +233,7 @@ export function Form16CreditNotes() {
|
||||
<Button variant="outline" onClick={() => setPreviewOpen(false)}>
|
||||
Close
|
||||
</Button>
|
||||
<Button onClick={downloadPreviewCsv} disabled={!preview || previewLoading}>
|
||||
<Button onClick={downloadPreviewCsv} disabled={!preview || previewLoading || !preview?.downloadUrl}>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Download CSV
|
||||
</Button>
|
||||
|
||||
@ -9,8 +9,9 @@ import { Search, Loader2, Receipt, RefreshCw, Eye, FileText, IndianRupee, Calend
|
||||
import { toast } from 'sonner';
|
||||
import {
|
||||
listDebitNotes,
|
||||
getDebitNoteSapResponseUrl,
|
||||
getDebitNoteSapResponse,
|
||||
type Form16DebitNoteListItem,
|
||||
type Form16SapResponseRecord,
|
||||
type ListDebitNotesParams,
|
||||
type ListDebitNotesSummary,
|
||||
} from '@/services/form16Api';
|
||||
@ -43,34 +44,14 @@ function formatIssuedAt(value: string | null | undefined): string {
|
||||
}
|
||||
}
|
||||
|
||||
function buildAbsoluteBackendUrl(url: string): string {
|
||||
if (!url) return url;
|
||||
if (/^https?:\/\//i.test(url)) return url;
|
||||
const apiBase = ((import.meta as any).env?.VITE_API_BASE_URL as string | undefined) || 'http://localhost:5000/api/v1';
|
||||
const origin = apiBase.replace(/\/api\/v1\/?$/i, '');
|
||||
return `${origin}${url.startsWith('/') ? '' : '/'}${url}`;
|
||||
}
|
||||
|
||||
function parseSapResponseCsv(rawCsv: string): Record<string, string> | null {
|
||||
const lines = rawCsv
|
||||
.split(/\r?\n/)
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean);
|
||||
if (lines.length < 2) return null;
|
||||
|
||||
const header = lines[0]!.split('|').map((h) => h.trim());
|
||||
// Debit response should contain: TRNS_UNIQ_NO, CLAIM_NUMBER, DOC_NO, MSG_TYP, MESSAGE
|
||||
for (let i = lines.length - 1; i >= 1; i--) {
|
||||
const values = lines[i]!.split('|');
|
||||
const obj: Record<string, string> = {};
|
||||
header.forEach((h, idx) => (obj[h] = (values[idx] || '').trim()));
|
||||
const trns = (obj.TRNS_UNIQ_NO || '').trim();
|
||||
const claim = (obj.CLAIM_NUMBER || '').trim();
|
||||
const doc = (obj.DOC_NO || '').trim();
|
||||
const typ = (obj.MSG_TYP || '').trim();
|
||||
if (trns || (claim && (doc || typ))) return obj;
|
||||
}
|
||||
return null;
|
||||
function mapSapResponseToPreviewRow(sap: Form16SapResponseRecord): Record<string, string> {
|
||||
return {
|
||||
TRNS_UNIQ_NO: sap.trnsUniqNo || '',
|
||||
TDS_TRNS_ID: sap.tdsTransId || sap.claimNumber || '',
|
||||
DOC_NO: sap.sapDocumentNumber || '',
|
||||
MSG_TYP: sap.msgTyp || '',
|
||||
MESSAGE: sap.message || '',
|
||||
};
|
||||
}
|
||||
|
||||
const DEFAULT_SUMMARY: ListDebitNotesSummary = {
|
||||
@ -129,13 +110,8 @@ export function Form16DebitNotes() {
|
||||
setPreviewRow(null);
|
||||
setPreviewMeta({ amountText: formatAmount(note.amount), issuedAtText: formatIssuedAt(note.issueDate) });
|
||||
try {
|
||||
const url = await getDebitNoteSapResponseUrl(Number(note.id));
|
||||
const absUrl = buildAbsoluteBackendUrl(url);
|
||||
const res = await apiClient.get<Blob>(absUrl, { responseType: 'blob' });
|
||||
const rawCsv = await res.data.text();
|
||||
const row = parseSapResponseCsv(rawCsv);
|
||||
if (!row) throw new Error('Could not parse SAP response CSV');
|
||||
setPreviewRow(row);
|
||||
const payload = await getDebitNoteSapResponse(Number(note.id));
|
||||
setPreviewRow(mapSapResponseToPreviewRow(payload.sapResponse));
|
||||
} catch (e: any) {
|
||||
const msg =
|
||||
e?.response?.status === 409
|
||||
@ -195,7 +171,7 @@ export function Form16DebitNotes() {
|
||||
</div>
|
||||
) : previewRow ? (
|
||||
<div className="space-y-4">
|
||||
{/* Block 1 (green): TRNS_UNIQ_NO + CLAIM_NUMBER */}
|
||||
{/* Block 1 (green): TRNS_UNIQ_NO + TDS_TRNS_ID */}
|
||||
<div className="rounded-xl border border-emerald-200 bg-emerald-50/70 p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<FileText className="w-4 h-4 text-emerald-700" />
|
||||
@ -207,8 +183,8 @@ export function Form16DebitNotes() {
|
||||
<p className="text-sm font-semibold text-emerald-900 break-all mt-1">{previewRow['TRNS_UNIQ_NO'] || '–'}</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-emerald-200 bg-white/70 p-3">
|
||||
<p className="text-xs text-emerald-700 uppercase tracking-wide">CLAIM_NUMBER</p>
|
||||
<p className="text-sm font-semibold text-emerald-900 break-all mt-1">{previewRow['CLAIM_NUMBER'] || '–'}</p>
|
||||
<p className="text-xs text-emerald-700 uppercase tracking-wide">TDS_TRNS_ID</p>
|
||||
<p className="text-sm font-semibold text-emerald-900 break-all mt-1">{previewRow['TDS_TRNS_ID'] || '–'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -124,6 +124,26 @@ export interface ListDebitNotesParams {
|
||||
quarter?: string;
|
||||
}
|
||||
|
||||
export interface Form16SapResponseRecord {
|
||||
fileName: string | null;
|
||||
trnsUniqNo: string | null;
|
||||
tdsTransId: string | null;
|
||||
claimNumber: string | null;
|
||||
sapDocumentNumber: string | null;
|
||||
msgTyp: string | null;
|
||||
message: string | null;
|
||||
docDate: string | null;
|
||||
tdsAmt: string | null;
|
||||
storageUrl: string | null;
|
||||
createdAt: string | null;
|
||||
updatedAt: string | null;
|
||||
}
|
||||
|
||||
export interface Form16SapResponsePayload {
|
||||
sapResponse: Form16SapResponseRecord;
|
||||
url?: string | null;
|
||||
}
|
||||
|
||||
export interface Form16Permissions {
|
||||
canViewForm16Submission: boolean;
|
||||
canView26AS: boolean;
|
||||
@ -186,6 +206,20 @@ export async function getDebitNoteSapResponseUrl(id: number): Promise<string> {
|
||||
return url;
|
||||
}
|
||||
|
||||
export async function getDebitNoteSapResponse(id: number): Promise<Form16SapResponsePayload> {
|
||||
const { data } = await apiClient.get<{ data?: Form16SapResponsePayload } | Form16SapResponsePayload>(`/form16/debit-notes/${id}/sap-response`);
|
||||
const payload = (data && typeof data === 'object' && 'data' in data ? data.data : data) as Form16SapResponsePayload | undefined;
|
||||
if (!payload?.sapResponse) throw new Error('SAP response not available');
|
||||
return payload;
|
||||
}
|
||||
|
||||
export async function getCreditNoteSapResponse(id: number): Promise<Form16SapResponsePayload> {
|
||||
const { data } = await apiClient.get<{ data?: Form16SapResponsePayload } | Form16SapResponsePayload>(`/form16/credit-notes/${id}/sap-response`);
|
||||
const payload = (data && typeof data === 'object' && 'data' in data ? data.data : data) as Form16SapResponsePayload | undefined;
|
||||
if (!payload?.sapResponse) throw new Error('SAP response not available');
|
||||
return payload;
|
||||
}
|
||||
|
||||
/** Get credit note linked to a Form 16 request (for workflow tab). */
|
||||
export async function getCreditNoteByRequestId(requestId: string): Promise<Form16CreditNoteItem | null> {
|
||||
const { data } = await apiClient.get<{ data?: { creditNote?: Form16CreditNoteItem | null }; creditNote?: Form16CreditNoteItem | null }>(`/form16/requests/${encodeURIComponent(requestId)}/credit-note`);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user