307 lines
12 KiB
TypeScript
307 lines
12 KiB
TypeScript
/**
|
||
* E-Invoice (IRP/NIC/PWC) Error Code Mapping
|
||
* Maps technical error codes from IRP/PWC API to user-friendly messages
|
||
*
|
||
* Sources: NIC IRP Portal, ClearTax, GST Portal documentation
|
||
*/
|
||
|
||
interface EInvoiceErrorInfo {
|
||
/** User-friendly message shown in the UI toast */
|
||
userMessage: string;
|
||
/** Suggested action for the user */
|
||
action?: string;
|
||
}
|
||
|
||
/**
|
||
* Comprehensive mapping of IRP/NIC/PWC error codes to user-friendly messages
|
||
* Error codes come from the IRP (Invoice Registration Portal) managed by NIC
|
||
*/
|
||
const ERROR_CODE_MAP: Record<string, EInvoiceErrorInfo> = {
|
||
// ── Duplicate / Already Exists ──
|
||
'2150': {
|
||
userMessage: 'This invoice has already been registered. An IRN was previously generated for this invoice.',
|
||
action: 'No action needed — the existing IRN will be used.',
|
||
},
|
||
'2295': {
|
||
userMessage: 'This invoice was already submitted to another IRP portal.',
|
||
action: 'Ensure invoices are not submitted to multiple IRP portals simultaneously.',
|
||
},
|
||
'DUPIRN': {
|
||
userMessage: 'Duplicate IRN detected — this invoice was already registered.',
|
||
action: 'The existing IRN will be used automatically.',
|
||
},
|
||
|
||
// ── GSTIN Errors ──
|
||
'2117': {
|
||
userMessage: 'The supplier GSTIN is invalid or not active on the GST portal.',
|
||
action: 'Please verify the dealer GSTIN is correct and active.',
|
||
},
|
||
'2118': {
|
||
userMessage: 'The buyer GSTIN is invalid or not active on the GST portal.',
|
||
action: 'Please verify the buyer GSTIN is correct and active.',
|
||
},
|
||
'2148': {
|
||
userMessage: 'The GSTIN provided is not registered for e-invoicing.',
|
||
action: 'Ensure the GSTIN is registered and eligible for e-invoice generation.',
|
||
},
|
||
'2163': {
|
||
userMessage: 'Invalid supplier GSTIN format.',
|
||
action: 'Please check the dealer GSTIN — it should be a valid 15-character GSTIN.',
|
||
},
|
||
'2164': {
|
||
userMessage: 'Invalid buyer GSTIN format.',
|
||
action: 'Please verify the buyer GSTIN format.',
|
||
},
|
||
'2166': {
|
||
userMessage: 'The supplier GSTIN is not found in the e-invoice master database.',
|
||
action: 'The dealer GSTIN needs to be registered in the e-invoice system. Please contact support.',
|
||
},
|
||
'701': {
|
||
userMessage: 'The supplier GSTIN is not registered in the e-invoice master.',
|
||
action: 'Please ensure the GSTIN is registered and synced with the e-invoice portal.',
|
||
},
|
||
|
||
// ── Supplier/Buyer Issues ──
|
||
'2160': {
|
||
userMessage: 'The supplier and buyer GSTIN cannot be the same.',
|
||
action: 'Ensure the dealer GSTIN and Royal Enfield GSTIN are different.',
|
||
},
|
||
'2165': {
|
||
userMessage: 'Buyer GSTIN cannot be "URP" (Unregistered Person) for B2B supply type.',
|
||
action: 'Please provide a valid GSTIN for the buyer.',
|
||
},
|
||
|
||
// ── State Code / Location ──
|
||
'2161': {
|
||
userMessage: 'The state code in the GSTIN does not match the state code in the address.',
|
||
action: 'Verify that the dealer state code matches the first 2 digits of their GSTIN.',
|
||
},
|
||
'2167': {
|
||
userMessage: 'Invalid PIN code provided for the supplier or buyer.',
|
||
action: 'Please check the PIN code in the dealer address details.',
|
||
},
|
||
'2168': {
|
||
userMessage: 'The PIN code does not match the state code.',
|
||
action: 'Ensure the PIN code belongs to the correct state as per the GSTIN.',
|
||
},
|
||
|
||
// ── Tax Calculation Errors ──
|
||
'2172': {
|
||
userMessage: 'IGST amount cannot be applied for intra-state (same state) transactions.',
|
||
action: 'For same-state transactions, use CGST + SGST instead of IGST.',
|
||
},
|
||
'2173': {
|
||
userMessage: 'CGST/SGST amounts are not applicable for inter-state transactions.',
|
||
action: 'For different-state transactions, use IGST instead of CGST + SGST.',
|
||
},
|
||
'2174': {
|
||
userMessage: 'Invalid CGST or SGST amount — they must be equal.',
|
||
action: 'Please ensure CGST and SGST amounts are exactly equal (half of the total GST each).',
|
||
},
|
||
'2175': {
|
||
userMessage: 'Invalid IGST amount for the given item.',
|
||
action: 'Verify the IGST amount = Taxable Value × GST Rate.',
|
||
},
|
||
|
||
// ── HSN / Item Errors ──
|
||
'2176': {
|
||
userMessage: 'Invalid HSN code provided for one or more items.',
|
||
action: 'Please verify the HSN/SAC code from the official GST HSN list.',
|
||
},
|
||
'2178': {
|
||
userMessage: 'Invalid GST rate for the given HSN code.',
|
||
action: 'Please ensure the correct GST rate is applied for the HSN/SAC code.',
|
||
},
|
||
'2179': {
|
||
userMessage: 'Duplicate item serial numbers found in the invoice.',
|
||
action: 'Each line item must have a unique serial number.',
|
||
},
|
||
|
||
// ── Value Mismatch ──
|
||
'2182': {
|
||
userMessage: 'Total taxable value does not match the sum of individual line items.',
|
||
action: 'Please check that the total taxable value equals the sum of all item amounts.',
|
||
},
|
||
'2189': {
|
||
userMessage: 'Total invoice value does not match the sum of item values.',
|
||
action: 'Ensure total invoice value = taxable value + all taxes + cess + other charges.',
|
||
},
|
||
'2188': {
|
||
userMessage: 'Invalid total item value — must equal assessable value + taxes.',
|
||
action: 'Recalculate the item total to include base amount and all applicable taxes.',
|
||
},
|
||
|
||
// ── Document Errors ──
|
||
'2153': {
|
||
userMessage: 'Invalid invoice number format.',
|
||
action: 'Invoice number should contain only alphanumeric characters, hyphens, and slashes.',
|
||
},
|
||
'2155': {
|
||
userMessage: 'Invoice date is invalid or in the future.',
|
||
action: 'Please provide a valid invoice date that is not in the future.',
|
||
},
|
||
'2157': {
|
||
userMessage: 'The invoice date is older than the allowed limit.',
|
||
action: 'E-invoices can only be generated for recent invoices as per GST guidelines.',
|
||
},
|
||
|
||
// ── Authentication ──
|
||
'1005': {
|
||
userMessage: 'Authentication with the e-invoice portal failed.',
|
||
action: 'Please try again. If the issue persists, contact the system administrator.',
|
||
},
|
||
'1004': {
|
||
userMessage: 'E-invoice portal session expired.',
|
||
action: 'Please try again — a new session will be created automatically.',
|
||
},
|
||
|
||
// ── System / Network ──
|
||
'404': {
|
||
userMessage: 'The e-invoice service is temporarily unavailable.',
|
||
action: 'Please try again after a few minutes.',
|
||
},
|
||
'500': {
|
||
userMessage: 'The e-invoice portal encountered an internal error.',
|
||
action: 'Please try again. If the issue persists, contact support.',
|
||
},
|
||
'503': {
|
||
userMessage: 'The e-invoice portal is currently under maintenance.',
|
||
action: 'Please try again after some time.',
|
||
},
|
||
};
|
||
|
||
/**
|
||
* Common keywords in PWC validation remarks → user-friendly messages
|
||
*/
|
||
const KEYWORD_MAP: Array<{ pattern: RegExp; userMessage: string; action?: string }> = [
|
||
{
|
||
pattern: /gstin.*not\s*(found|registered|valid|present)/i,
|
||
userMessage: 'The dealer GSTIN is not registered in the e-invoice system.',
|
||
action: 'Please verify the dealer GSTIN or contact support to register it.',
|
||
},
|
||
{
|
||
pattern: /duplicate\s*irn/i,
|
||
userMessage: 'This invoice was already registered and an IRN exists.',
|
||
action: 'The existing IRN will be used.',
|
||
},
|
||
{
|
||
pattern: /hsn.*invalid|invalid.*hsn/i,
|
||
userMessage: 'The HSN/SAC code provided is not valid.',
|
||
action: 'Please check the HSN code from the GST portal.',
|
||
},
|
||
{
|
||
pattern: /pin\s*code.*invalid|invalid.*pin/i,
|
||
userMessage: 'The PIN code in the address is invalid.',
|
||
action: 'Please update the dealer address with a valid PIN code.',
|
||
},
|
||
{
|
||
pattern: /tax.*mismatch|mismatch.*tax|amount.*mismatch/i,
|
||
userMessage: 'Tax amount does not match the expected calculation.',
|
||
action: 'Please verify that the tax amounts are correctly calculated based on the taxable value and GST rate.',
|
||
},
|
||
{
|
||
pattern: /igst.*intra|intra.*igst/i,
|
||
userMessage: 'IGST cannot be applied for same-state (intra-state) transactions.',
|
||
action: 'Use CGST + SGST for same-state transactions.',
|
||
},
|
||
{
|
||
pattern: /supplier.*buyer.*same|same.*gstin/i,
|
||
userMessage: 'Supplier and buyer GSTIN cannot be the same.',
|
||
action: 'Ensure the dealer and buyer GSTINs are different.',
|
||
},
|
||
{
|
||
pattern: /authentication|auth.*fail|token.*invalid|unauthorized/i,
|
||
userMessage: 'E-invoice portal authentication failed.',
|
||
action: 'Please try again. If the issue persists, contact the administrator.',
|
||
},
|
||
{
|
||
pattern: /timeout|timed?\s*out|connection.*refused/i,
|
||
userMessage: 'Could not connect to the e-invoice portal.',
|
||
action: 'Please check your internet connection and try again.',
|
||
},
|
||
{
|
||
pattern: /invoice.*date.*future|future.*date/i,
|
||
userMessage: 'Invoice date cannot be in the future.',
|
||
action: 'Please set the invoice date to today or a past date.',
|
||
},
|
||
{
|
||
pattern: /request.*not\s*found|not\s*found/i,
|
||
userMessage: 'The claim request was not found.',
|
||
action: 'Please refresh the page and try again.',
|
||
},
|
||
{
|
||
pattern: /dealer.*activity.*missing|missing.*dealer/i,
|
||
userMessage: 'Dealer or activity details are missing for this request.',
|
||
action: 'Please ensure the dealer and activity information is complete before generating the invoice.',
|
||
},
|
||
{
|
||
pattern: /claim\s*details?\s*not\s*found/i,
|
||
userMessage: 'Claim details not found for this request.',
|
||
action: 'Please ensure the claim proposal has been submitted.',
|
||
},
|
||
{
|
||
pattern: /cannot\s*generate.*currently\s*at\s*step/i,
|
||
userMessage: 'Cannot generate the invoice at this stage.',
|
||
action: 'Please complete all previous approval steps before generating the e-invoice.',
|
||
},
|
||
];
|
||
|
||
/**
|
||
* Translate a raw PWC/IRP error message into a user-friendly message
|
||
*
|
||
* @param rawError The raw error string from PWC/IRP response
|
||
* @returns User-friendly error message suitable for toast display
|
||
*/
|
||
export function translateEInvoiceError(rawError: string): string {
|
||
if (!rawError) return 'E-Invoice generation failed. Please try again.';
|
||
|
||
// 1. Try exact error code match
|
||
// Extract error codes like "2150", "701" from messages like "2150: Duplicate IRN"
|
||
const codeMatch = rawError.match(/\b(\d{3,4})\b/);
|
||
if (codeMatch) {
|
||
const code = codeMatch[1];
|
||
const mapped = ERROR_CODE_MAP[code];
|
||
if (mapped) {
|
||
return mapped.action
|
||
? `${mapped.userMessage} ${mapped.action}`
|
||
: mapped.userMessage;
|
||
}
|
||
}
|
||
|
||
// Also check for named codes like "DUPIRN"
|
||
const namedCodeMatch = rawError.match(/\b(DUPIRN)\b/i);
|
||
if (namedCodeMatch) {
|
||
const mapped = ERROR_CODE_MAP[namedCodeMatch[1].toUpperCase()];
|
||
if (mapped) {
|
||
return mapped.action
|
||
? `${mapped.userMessage} ${mapped.action}`
|
||
: mapped.userMessage;
|
||
}
|
||
}
|
||
|
||
// 2. Try keyword-based matching
|
||
for (const entry of KEYWORD_MAP) {
|
||
if (entry.pattern.test(rawError)) {
|
||
return entry.action
|
||
? `${entry.userMessage} ${entry.action}`
|
||
: entry.userMessage;
|
||
}
|
||
}
|
||
|
||
// 3. Fallback: clean up the raw message for display
|
||
// Remove internal prefixes like "[PWC]", "Failed to generate signed e-invoice via PWC:"
|
||
let cleaned = rawError
|
||
.replace(/\[PWC\]\s*/gi, '')
|
||
.replace(/\[PWCIntegration\]\s*/gi, '')
|
||
.replace(/Failed to generate signed e-invoice via PWC:\s*/gi, '')
|
||
.replace(/E-Invoice generation failed:\s*/gi, '')
|
||
.trim();
|
||
|
||
// If the cleaned message is still very technical, provide a generic one
|
||
if (cleaned.length > 200 || /stack\s*trace|at\s+\w+\.\w+\s*\(/i.test(cleaned)) {
|
||
return 'E-Invoice generation failed due to a validation error. Please verify the claim details (dealer GSTIN, amounts, HSN codes) and try again.';
|
||
}
|
||
|
||
return cleaned || 'E-Invoice generation failed. Please try again.';
|
||
}
|