diff --git a/src/jobs/form16SapResponseJob.ts b/src/jobs/form16SapResponseJob.ts index 7f9b69c..cb27342 100644 --- a/src/jobs/form16SapResponseJob.ts +++ b/src/jobs/form16SapResponseJob.ts @@ -60,6 +60,7 @@ function extractCsvFields(r: Record) { async function findCreditNoteId( trnsUniqNo: string | null, tdsTransId: string | null, + claimNumber: string | null, fileName: string, ): Promise<{ creditNoteId: number | null; requestId: string | null }> { const CN = Form16CreditNote as any; @@ -93,6 +94,12 @@ async function findCreditNoteId( } } + // 4. CLAIM_NUMBER = credit note number (seen in some SAP/WFM exports) + if (!cn && claimNumber) { + cn = await CN.findOne({ where: { creditNoteNumber: claimNumber }, attributes: ['id', 'submissionId'] }); + if (cn) logger.info(`[Form16 SAP Job] Credit match via CLAIM_NUMBER=${claimNumber} → credit_note id=${cn.id}`); + } + if (!cn) return { creditNoteId: null, requestId: null }; const submission = await (Form16aSubmission as any).findByPk(cn.submissionId, { attributes: ['requestId'] }); @@ -225,7 +232,7 @@ async function processOutgoingFile( let requestNumber: string | null = null; if (type === 'credit') { - const res = await findCreditNoteId(trnsUniqNo, tdsTransId, fileName); + const res = await findCreditNoteId(trnsUniqNo, tdsTransId, claimNumber, fileName); creditNoteId = res.creditNoteId; requestId = res.requestId; if (creditNoteId && sapDocNo) { diff --git a/src/services/form16.service.ts b/src/services/form16.service.ts index 36fca88..a326510 100644 --- a/src/services/form16.service.ts +++ b/src/services/form16.service.ts @@ -278,7 +278,11 @@ export async function listCreditNotesForDealer(userId: string, filters?: { finan if (hasTrnsUniqNoColumn && noteIds.length) { try { const sapRows = await (Form16SapResponse as any).findAll({ - where: { type: 'credit', creditNoteId: { [Op.in]: noteIds }, storageUrl: { [Op.ne]: null } }, + where: { + type: 'credit', + creditNoteId: { [Op.in]: noteIds }, + [Op.or]: [{ storageUrl: { [Op.ne]: null } }, { sapDocumentNumber: { [Op.ne]: null } }], + }, attributes: ['creditNoteId'], raw: true, }); @@ -883,7 +887,11 @@ export async function listAllCreditNotesForRe(filters?: { financialYear?: string if (hasTrnsUniqNoColumn && noteIds.length) { try { const sapRows = await (Form16SapResponse as any).findAll({ - where: { type: 'credit', creditNoteId: { [Op.in]: noteIds }, storageUrl: { [Op.ne]: null } }, + where: { + type: 'credit', + creditNoteId: { [Op.in]: noteIds }, + [Op.or]: [{ storageUrl: { [Op.ne]: null } }, { sapDocumentNumber: { [Op.ne]: null } }], + }, attributes: ['creditNoteId'], raw: true, }); @@ -995,7 +1003,10 @@ export async function listAllDebitNotesForRe(filters?: { financialYear?: string; if (noteIds.length) { try { const sapRows = await (Form16DebitNoteSapResponse as any).findAll({ - where: { debitNoteId: { [Op.in]: noteIds }, storageUrl: { [Op.ne]: null } }, + where: { + debitNoteId: { [Op.in]: noteIds }, + [Op.or]: [{ storageUrl: { [Op.ne]: null } }, { sapDocumentNumber: { [Op.ne]: null } }], + }, attributes: ['debitNoteId'], raw: true, }); diff --git a/src/services/wfmFile.service.ts b/src/services/wfmFile.service.ts index d5e4598..4fa2194 100644 --- a/src/services/wfmFile.service.ts +++ b/src/services/wfmFile.service.ts @@ -287,9 +287,21 @@ export class WFMFileService { const fileContent = fs.readFileSync(filePath, 'utf-8'); const lines = fileContent.split(/\r?\n/).filter(line => line.trim() !== ''); if (lines.length <= 1) return []; - const headers = lines[0].split('|').map(h => h.trim()); + + // SAP/WFM responses are expected to use pipe ('|'), but some environments may export + // with comma/semicolon or with spaces in header names. Normalize both delimiter + headers. + const headerLine = lines[0] || ''; + const delimiter = headerLine.includes('|') ? '|' : headerLine.includes(';') ? ';' : ','; + const normalizeHeaderKey = (h: string) => + h + .trim() + .toUpperCase() + .replace(/\s+/g, '_') // 'DOC NO' -> 'DOC_NO' + .replace(/[^A-Z0-9_]/g, '_'); // keep only safe chars for matching + + const headers = headerLine.split(delimiter).map(normalizeHeaderKey); const data = lines.slice(1).map(line => { - const values = line.split('|'); + const values = line.split(delimiter); const row: any = {}; headers.forEach((header, index) => { row[header] = values[index]?.trim() || '';