inplemented the gst and non gst invoice generation flow and cost item table enhanced
This commit is contained in:
parent
896b345e02
commit
9fd9c218df
@ -11,6 +11,11 @@ import { sapIntegrationService } from '../services/sapIntegration.service';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
import { WorkflowRequest } from '../models/WorkflowRequest';
|
||||||
|
import { DealerClaimDetails } from '../models/DealerClaimDetails';
|
||||||
|
import { ClaimInvoice } from '../models/ClaimInvoice';
|
||||||
|
import { ClaimInvoiceItem } from '../models/ClaimInvoiceItem';
|
||||||
|
import { ActivityType } from '../models/ActivityType';
|
||||||
|
|
||||||
export class DealerClaimController {
|
export class DealerClaimController {
|
||||||
private dealerClaimService = new DealerClaimService();
|
private dealerClaimService = new DealerClaimService();
|
||||||
@ -981,5 +986,91 @@ export class DealerClaimController {
|
|||||||
return ResponseHandler.error(res, error.message || 'Failed to test SAP budget block', 500);
|
return ResponseHandler.error(res, error.message || 'Failed to test SAP budget block', 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Download Invoice CSV
|
||||||
|
* GET /api/v1/dealer-claims/:requestId/e-invoice/csv
|
||||||
|
*/
|
||||||
|
async downloadInvoiceCsv(req: Request, res: Response): Promise<void> {
|
||||||
|
try {
|
||||||
|
const identifier = req.params.requestId;
|
||||||
|
|
||||||
|
// Use helper to find workflow
|
||||||
|
const workflow = await this.findWorkflowByIdentifier(identifier);
|
||||||
|
if (!workflow) {
|
||||||
|
return ResponseHandler.error(res, 'Workflow request not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const requestId = (workflow as any).requestId || (workflow as any).request_id;
|
||||||
|
const requestNumber = (workflow as any).requestNumber || (workflow as any).request_number;
|
||||||
|
|
||||||
|
// Fetch related data
|
||||||
|
logger.info(`[DealerClaimController] Preparing CSV for requestId: ${requestId}`);
|
||||||
|
const [invoice, items, claimDetails, internalOrder] = await Promise.all([
|
||||||
|
ClaimInvoice.findOne({ where: { requestId } }),
|
||||||
|
ClaimInvoiceItem.findAll({ where: { requestId }, order: [['slNo', 'ASC']] }),
|
||||||
|
DealerClaimDetails.findOne({ where: { requestId } }),
|
||||||
|
InternalOrder.findOne({ where: { requestId } })
|
||||||
|
]);
|
||||||
|
|
||||||
|
logger.info(`[DealerClaimController] Found ${items.length} items to export for request ${requestNumber}`);
|
||||||
|
|
||||||
|
let sapRefNo = '';
|
||||||
|
if (claimDetails?.activityType) {
|
||||||
|
const activityType = await ActivityType.findOne({ where: { title: claimDetails.activityType } });
|
||||||
|
sapRefNo = activityType?.sapRefNo || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct CSV
|
||||||
|
const headers = [
|
||||||
|
'TRNS_UNIQ_NO',
|
||||||
|
'CLAIM_NUMBER',
|
||||||
|
'INV_NUMBER',
|
||||||
|
'DEALER_CODE',
|
||||||
|
'IO_NUMBER',
|
||||||
|
'CLAIM_DOC_TYP',
|
||||||
|
'CLAIM_DATE',
|
||||||
|
'CLAIM_AMT',
|
||||||
|
'GST_AMT',
|
||||||
|
'GST_PERCENTAG'
|
||||||
|
];
|
||||||
|
|
||||||
|
const rows = items.map(item => {
|
||||||
|
const trnsUniqNo = item.transactionCode || '';
|
||||||
|
const claimNumber = requestNumber;
|
||||||
|
const invNumber = invoice?.invoiceNumber || '';
|
||||||
|
const dealerCode = claimDetails?.dealerCode || '';
|
||||||
|
const ioNumber = internalOrder?.ioNumber || '';
|
||||||
|
const claimDocTyp = sapRefNo;
|
||||||
|
const claimDate = invoice?.createdAt ? new Date(invoice.createdAt).toISOString().split('T')[0] : '';
|
||||||
|
const claimAmt = item.assAmt;
|
||||||
|
const totalTax = Number(item.igstAmt || 0) + Number(item.cgstAmt || 0) + Number(item.sgstAmt || 0) + Number(item.utgstAmt || 0);
|
||||||
|
const gstPercentag = item.gstRt;
|
||||||
|
|
||||||
|
return [
|
||||||
|
trnsUniqNo,
|
||||||
|
claimNumber,
|
||||||
|
invNumber,
|
||||||
|
dealerCode,
|
||||||
|
ioNumber,
|
||||||
|
claimDocTyp,
|
||||||
|
claimDate,
|
||||||
|
claimAmt,
|
||||||
|
totalTax.toFixed(2),
|
||||||
|
gstPercentag
|
||||||
|
].join(',');
|
||||||
|
});
|
||||||
|
|
||||||
|
const csvContent = [headers.join(','), ...rows].join('\n');
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', 'text/csv');
|
||||||
|
res.setHeader('Content-Disposition', `attachment; filename="Invoice_${requestNumber}.csv"`);
|
||||||
|
|
||||||
|
res.status(200).send(csvContent);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||||
|
logger.error('[DealerClaimController] Error downloading invoice CSV:', error);
|
||||||
|
return ResponseHandler.error(res, 'Failed to download invoice CSV', 500, errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -70,6 +70,31 @@ module.exports = {
|
|||||||
type: DataTypes.DECIMAL(15, 2),
|
type: DataTypes.DECIMAL(15, 2),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
},
|
},
|
||||||
|
utgst_amt: {
|
||||||
|
type: DataTypes.DECIMAL(15, 2),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
cgst_rate: {
|
||||||
|
type: DataTypes.DECIMAL(5, 2),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
sgst_rate: {
|
||||||
|
type: DataTypes.DECIMAL(5, 2),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
igst_rate: {
|
||||||
|
type: DataTypes.DECIMAL(5, 2),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
utgst_rate: {
|
||||||
|
type: DataTypes.DECIMAL(5, 2),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
tot_item_val: {
|
tot_item_val: {
|
||||||
type: DataTypes.DECIMAL(15, 2),
|
type: DataTypes.DECIMAL(15, 2),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
|
|||||||
@ -18,14 +18,19 @@ interface ClaimInvoiceItemAttributes {
|
|||||||
igstAmt: number;
|
igstAmt: number;
|
||||||
cgstAmt: number;
|
cgstAmt: number;
|
||||||
sgstAmt: number;
|
sgstAmt: number;
|
||||||
|
utgstAmt: number;
|
||||||
totItemVal: number;
|
totItemVal: number;
|
||||||
isServc: string;
|
isServc: string;
|
||||||
|
igstRate?: number;
|
||||||
|
cgstRate?: number;
|
||||||
|
sgstRate?: number;
|
||||||
|
utgstRate?: number;
|
||||||
expenseIds?: string[];
|
expenseIds?: string[];
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClaimInvoiceItemCreationAttributes extends Optional<ClaimInvoiceItemAttributes, 'itemId' | 'invoiceNumber' | 'transactionCode' | 'expenseIds' | 'createdAt' | 'updatedAt'> { }
|
interface ClaimInvoiceItemCreationAttributes extends Optional<ClaimInvoiceItemAttributes, 'itemId' | 'invoiceNumber' | 'transactionCode' | 'expenseIds' | 'createdAt' | 'updatedAt' | 'utgstAmt' | 'igstRate' | 'cgstRate' | 'sgstRate' | 'utgstRate'> { }
|
||||||
|
|
||||||
class ClaimInvoiceItem extends Model<ClaimInvoiceItemAttributes, ClaimInvoiceItemCreationAttributes> implements ClaimInvoiceItemAttributes {
|
class ClaimInvoiceItem extends Model<ClaimInvoiceItemAttributes, ClaimInvoiceItemCreationAttributes> implements ClaimInvoiceItemAttributes {
|
||||||
public itemId!: string;
|
public itemId!: string;
|
||||||
@ -43,8 +48,13 @@ class ClaimInvoiceItem extends Model<ClaimInvoiceItemAttributes, ClaimInvoiceIte
|
|||||||
public igstAmt!: number;
|
public igstAmt!: number;
|
||||||
public cgstAmt!: number;
|
public cgstAmt!: number;
|
||||||
public sgstAmt!: number;
|
public sgstAmt!: number;
|
||||||
|
public utgstAmt!: number;
|
||||||
public totItemVal!: number;
|
public totItemVal!: number;
|
||||||
public isServc!: string;
|
public isServc!: string;
|
||||||
|
public igstRate?: number;
|
||||||
|
public cgstRate?: number;
|
||||||
|
public sgstRate?: number;
|
||||||
|
public utgstRate?: number;
|
||||||
public expenseIds?: string[];
|
public expenseIds?: string[];
|
||||||
public createdAt!: Date;
|
public createdAt!: Date;
|
||||||
public updatedAt!: Date;
|
public updatedAt!: Date;
|
||||||
@ -134,6 +144,36 @@ ClaimInvoiceItem.init(
|
|||||||
allowNull: false,
|
allowNull: false,
|
||||||
field: 'sgst_amt',
|
field: 'sgst_amt',
|
||||||
},
|
},
|
||||||
|
utgstAmt: {
|
||||||
|
type: DataTypes.DECIMAL(15, 2),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
field: 'utgst_amt',
|
||||||
|
},
|
||||||
|
igstRate: {
|
||||||
|
type: DataTypes.DECIMAL(5, 2),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
field: 'igst_rate',
|
||||||
|
},
|
||||||
|
cgstRate: {
|
||||||
|
type: DataTypes.DECIMAL(5, 2),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
field: 'cgst_rate',
|
||||||
|
},
|
||||||
|
sgstRate: {
|
||||||
|
type: DataTypes.DECIMAL(5, 2),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
field: 'sgst_rate',
|
||||||
|
},
|
||||||
|
utgstRate: {
|
||||||
|
type: DataTypes.DECIMAL(5, 2),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
field: 'utgst_rate',
|
||||||
|
},
|
||||||
totItemVal: {
|
totItemVal: {
|
||||||
type: DataTypes.DECIMAL(15, 2),
|
type: DataTypes.DECIMAL(15, 2),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
|
|||||||
@ -94,7 +94,8 @@ router.get('/:requestId/e-invoice/pdf', authenticateToken, asyncHandler(dealerCl
|
|||||||
* @desc Update credit note details (Step 8)
|
* @desc Update credit note details (Step 8)
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.put('/:requestId/credit-note', authenticateToken, asyncHandler(dealerClaimController.updateCreditNote.bind(dealerClaimController)));
|
router.get('/:requestId/e-invoice/csv', authenticateToken, asyncHandler(dealerClaimController.downloadInvoiceCsv.bind(dealerClaimController)));
|
||||||
|
router.post('/:requestId/credit-note', authenticateToken, upload.single('creditNoteFile'), asyncHandler(dealerClaimController.updateCreditNote.bind(dealerClaimController)));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/v1/dealer-claims/:requestId/credit-note/send
|
* @route POST /api/v1/dealer-claims/:requestId/credit-note/send
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import { User } from '../models/User';
|
|||||||
import { DealerClaimHistory, SnapshotType } from '../models/DealerClaimHistory';
|
import { DealerClaimHistory, SnapshotType } from '../models/DealerClaimHistory';
|
||||||
import { ActivityType } from '../models/ActivityType';
|
import { ActivityType } from '../models/ActivityType';
|
||||||
import { Document } from '../models/Document';
|
import { Document } from '../models/Document';
|
||||||
|
import { Dealer } from '../models/Dealer';
|
||||||
import { WorkflowService } from './workflow.service';
|
import { WorkflowService } from './workflow.service';
|
||||||
import { DealerClaimApprovalService } from './dealerClaimApproval.service';
|
import { DealerClaimApprovalService } from './dealerClaimApproval.service';
|
||||||
import { generateRequestNumber } from '../utils/helpers';
|
import { generateRequestNumber } from '../utils/helpers';
|
||||||
@ -1090,6 +1091,20 @@ export class DealerClaimService {
|
|||||||
} else {
|
} else {
|
||||||
serializedClaimDetails.defaultGstRate = 18; // Fallback
|
serializedClaimDetails.defaultGstRate = 18; // Fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch dealer GSTIN from dealers table
|
||||||
|
try {
|
||||||
|
const dealer = await Dealer.findOne({
|
||||||
|
where: { dlrcode: claimDetails.dealerCode }
|
||||||
|
});
|
||||||
|
if (dealer) {
|
||||||
|
serializedClaimDetails.dealerGstin = dealer.gst || null;
|
||||||
|
// Also add for backward compatibility if needed
|
||||||
|
serializedClaimDetails.dealerGSTIN = dealer.gst || null;
|
||||||
|
}
|
||||||
|
} catch (dealerError) {
|
||||||
|
logger.warn(`[DealerClaimService] Error fetching dealer GSTIN for ${claimDetails.dealerCode}:`, dealerError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform proposal details to include cost items as array
|
// Transform proposal details to include cost items as array
|
||||||
@ -1484,14 +1499,18 @@ export class DealerClaimService {
|
|||||||
gstRate = 18; // Default fallback
|
gstRate = 18; // Default fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
const igstRate = isIGST ? gstRate : 0;
|
const hasUtgst = (Number(item.utgstRate) > 0 || Number(item.utgstAmt) > 0);
|
||||||
const cgstRate = !isIGST ? gstRate / 2 : 0;
|
|
||||||
const sgstRate = !isIGST ? gstRate / 2 : 0;
|
|
||||||
|
|
||||||
const igstAmt = isIGST ? (baseTotal * gstRate) / 100 : 0;
|
const finalIgstRate = isIGST ? (Number(item.igstRate) || gstRate) : 0;
|
||||||
const cgstAmt = !isIGST ? (baseTotal * gstRate) / 200 : 0;
|
const finalCgstRate = !isIGST ? (Number(item.cgstRate) || gstRate / 2) : 0;
|
||||||
const sgstAmt = !isIGST ? (baseTotal * gstRate) / 200 : 0;
|
const finalSgstRate = (!isIGST && !hasUtgst) ? (Number(item.sgstRate) || gstRate / 2) : 0;
|
||||||
const gstAmt = igstAmt + cgstAmt + sgstAmt;
|
const finalUtgstRate = (!isIGST && hasUtgst) ? (Number(item.utgstRate) || gstRate / 2) : 0;
|
||||||
|
|
||||||
|
const finalIgstAmt = isIGST ? (Number(item.igstAmt) || (baseTotal * finalIgstRate) / 100) : 0;
|
||||||
|
const finalCgstAmt = !isIGST ? (Number(item.cgstAmt) || (baseTotal * finalCgstRate) / 100) : 0;
|
||||||
|
const finalSgstAmt = (!isIGST && !hasUtgst) ? (Number(item.sgstAmt) || (baseTotal * finalSgstRate) / 100) : 0;
|
||||||
|
const finalUtgstAmt = (!isIGST && hasUtgst) ? (Number(item.utgstAmt) || (baseTotal * finalUtgstRate) / 100) : 0;
|
||||||
|
const totalTaxAmt = finalIgstAmt + finalCgstAmt + finalSgstAmt + finalUtgstAmt;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
requestId,
|
requestId,
|
||||||
@ -1501,18 +1520,18 @@ export class DealerClaimService {
|
|||||||
quantity,
|
quantity,
|
||||||
hsnCode: item.hsnCode || '',
|
hsnCode: item.hsnCode || '',
|
||||||
gstRate,
|
gstRate,
|
||||||
gstAmt: Number(item.gstAmt) || gstAmt,
|
gstAmt: totalTaxAmt,
|
||||||
cgstRate: Number(item.cgstRate) || cgstRate,
|
cgstRate: finalCgstRate,
|
||||||
cgstAmt: Number(item.cgstAmt) || cgstAmt,
|
cgstAmt: finalCgstAmt,
|
||||||
sgstRate: Number(item.sgstRate) || sgstRate,
|
sgstRate: finalSgstRate,
|
||||||
sgstAmt: Number(item.sgstAmt) || sgstAmt,
|
sgstAmt: finalSgstAmt,
|
||||||
igstRate: Number(item.igstRate) || igstRate,
|
igstRate: finalIgstRate,
|
||||||
igstAmt: Number(item.igstAmt) || igstAmt,
|
igstAmt: finalIgstAmt,
|
||||||
utgstRate: Number(item.utgstRate) || 0,
|
utgstRate: finalUtgstRate,
|
||||||
utgstAmt: Number(item.utgstAmt) || 0,
|
utgstAmt: finalUtgstAmt,
|
||||||
cessRate: Number(item.cessRate) || 0,
|
cessRate: Number(item.cessRate) || 0,
|
||||||
cessAmt: Number(item.cessAmt) || 0,
|
cessAmt: Number(item.cessAmt) || 0,
|
||||||
totalAmt: Number(item.totalAmt) || (baseTotal + gstAmt),
|
totalAmt: Number(item.totalAmt) || (baseTotal + totalTaxAmt),
|
||||||
isService: !!item.isService,
|
isService: !!item.isService,
|
||||||
expenseDate: item.date instanceof Date ? item.date : (item.date ? new Date(item.date) : (completionData.activityCompletionDate || new Date())),
|
expenseDate: item.date instanceof Date ? item.date : (item.date ? new Date(item.date) : (completionData.activityCompletionDate || new Date())),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { DealerClaimDetails } from '../models/DealerClaimDetails';
|
|||||||
import { DealerCompletionExpense } from '../models/DealerCompletionExpense';
|
import { DealerCompletionExpense } from '../models/DealerCompletionExpense';
|
||||||
import { DealerCompletionDetails } from '../models/DealerCompletionDetails';
|
import { DealerCompletionDetails } from '../models/DealerCompletionDetails';
|
||||||
import { ClaimInvoiceItem } from '../models/ClaimInvoiceItem';
|
import { ClaimInvoiceItem } from '../models/ClaimInvoiceItem';
|
||||||
|
import { findDealerLocally, DealerInfo } from './dealer.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PWC E-Invoice Integration Service
|
* PWC E-Invoice Integration Service
|
||||||
@ -91,10 +92,11 @@ export class PWCIntegrationService {
|
|||||||
if (!request) return { success: false, error: 'Request not found' };
|
if (!request) return { success: false, error: 'Request not found' };
|
||||||
|
|
||||||
const claimDetails = (request as any).claimDetails;
|
const claimDetails = (request as any).claimDetails;
|
||||||
const dealer = await Dealer.findOne({ where: { dlrcode: claimDetails?.dealerCode } });
|
const dealer: DealerInfo | null = await findDealerLocally(claimDetails?.dealerCode, claimDetails?.dealerEmail);
|
||||||
const activity = await ActivityType.findOne({ where: { title: claimDetails?.activityType } });
|
const activity = await ActivityType.findOne({ where: { title: claimDetails?.activityType } });
|
||||||
|
|
||||||
if (!dealer || !activity) {
|
if (!dealer || !activity) {
|
||||||
|
logger.warn(`[PWCIntegration] Dealer or Activity missing for request ${requestId}. Dealer lookup: ${claimDetails?.dealerCode} / ${claimDetails?.dealerEmail}`);
|
||||||
return { success: false, error: 'Dealer or Activity details missing' };
|
return { success: false, error: 'Dealer or Activity details missing' };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,98 +111,8 @@ export class PWCIntegrationService {
|
|||||||
const formatQty = (val: number) => Number(val.toFixed(3));
|
const formatQty = (val: number) => Number(val.toFixed(3));
|
||||||
const formatRate = (val: number) => Number(val.toFixed(2));
|
const formatRate = (val: number) => Number(val.toFixed(2));
|
||||||
|
|
||||||
// NEW LOGIC: Check for Non-GST Activity
|
|
||||||
const isNonGST = activity.taxationType === 'Non GST';
|
|
||||||
|
|
||||||
if (isNonGST) {
|
|
||||||
logger.info(`[PWC] Activity ${activity.title} is Non-GST. Skipping IRN generation and grossing up values.`);
|
|
||||||
|
|
||||||
let totalNonGstVal = 0;
|
|
||||||
let nonGstSlNo = 1;
|
|
||||||
|
|
||||||
// Clear existing items
|
|
||||||
await ClaimInvoiceItem.destroy({ where: { requestId } });
|
|
||||||
|
|
||||||
if (expenses && expenses.length > 0) {
|
|
||||||
for (const expense of expenses) {
|
|
||||||
// Gross Up Logic: Total Amount (Base + Tax) becomes the new Base Amount
|
|
||||||
// stored in 'totalAmt' or calculated from components
|
|
||||||
const grossAmount = Number(expense.totalAmt) || (Number(expense.amount) + Number(expense.gstAmt || 0) + Number(expense.cessAmt || 0));
|
|
||||||
|
|
||||||
// Transaction Code
|
|
||||||
const transactionCode = `${customInvoiceNumber}-${String(nonGstSlNo).padStart(2, '0')}`;
|
|
||||||
|
|
||||||
await ClaimInvoiceItem.create({
|
|
||||||
requestId,
|
|
||||||
invoiceNumber: customInvoiceNumber,
|
|
||||||
transactionCode: transactionCode,
|
|
||||||
slNo: nonGstSlNo,
|
|
||||||
description: expense.description || activity.title,
|
|
||||||
hsnCd: expense.hsnCode || activity.hsnCode || "998311",
|
|
||||||
qty: 1,
|
|
||||||
unit: "NOS",
|
|
||||||
unitPrice: formatAmount(grossAmount),
|
|
||||||
assAmt: formatAmount(grossAmount), // Taxable value is the gross amount
|
|
||||||
gstRt: 0, // 0% Tax
|
|
||||||
igstAmt: 0,
|
|
||||||
cgstAmt: 0,
|
|
||||||
sgstAmt: 0,
|
|
||||||
totItemVal: formatAmount(grossAmount),
|
|
||||||
isServc: "Y", // Non-GST reimbursements are generally treated as service/settlement
|
|
||||||
expenseIds: [expense.expenseId]
|
|
||||||
});
|
|
||||||
|
|
||||||
totalNonGstVal += grossAmount;
|
|
||||||
nonGstSlNo++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Fallback if no specific expenses (rare)
|
|
||||||
const fallbackAmount = finalAmount;
|
|
||||||
const transactionCode = `${customInvoiceNumber}-${String(nonGstSlNo).padStart(2, '0')}`;
|
|
||||||
|
|
||||||
await ClaimInvoiceItem.create({
|
|
||||||
requestId,
|
|
||||||
invoiceNumber: customInvoiceNumber,
|
|
||||||
transactionCode: transactionCode,
|
|
||||||
slNo: nonGstSlNo,
|
|
||||||
description: activity.title,
|
|
||||||
hsnCd: activity.hsnCode || "998311",
|
|
||||||
qty: 1,
|
|
||||||
unit: "NOS",
|
|
||||||
unitPrice: formatAmount(fallbackAmount),
|
|
||||||
assAmt: formatAmount(fallbackAmount),
|
|
||||||
gstRt: 0,
|
|
||||||
igstAmt: 0,
|
|
||||||
cgstAmt: 0,
|
|
||||||
sgstAmt: 0,
|
|
||||||
totItemVal: formatAmount(fallbackAmount),
|
|
||||||
isServc: "Y",
|
|
||||||
expenseIds: []
|
|
||||||
});
|
|
||||||
totalNonGstVal += fallbackAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return internal success response (No IRN)
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
irn: undefined,
|
|
||||||
ackNo: undefined,
|
|
||||||
ackDate: new Date(),
|
|
||||||
signedInvoice: undefined,
|
|
||||||
qrCode: undefined, // No QR for Non-GST
|
|
||||||
qrImage: undefined,
|
|
||||||
totalIgstAmt: 0,
|
|
||||||
totalCgstAmt: 0,
|
|
||||||
totalSgstAmt: 0,
|
|
||||||
totalAssAmt: formatAmount(totalNonGstVal)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- END Non-GST Logic ---
|
|
||||||
|
|
||||||
// Existing GST Logic starts here...
|
|
||||||
// Extract State Code from Dealer GSTIN
|
// Extract State Code from Dealer GSTIN
|
||||||
let dealerGst = (dealer as any).gst;
|
let dealerGst = dealer?.gstin;
|
||||||
|
|
||||||
// HOTFIX: For PWC QA Environment, use a known valid GSTIN if dealer has the invalid test one
|
// HOTFIX: For PWC QA Environment, use a known valid GSTIN if dealer has the invalid test one
|
||||||
// The test GSTIN 29AAACE3882D1ZZ is not registered in PWC QA Master, causing Error 701
|
// The test GSTIN 29AAACE3882D1ZZ is not registered in PWC QA Master, causing Error 701
|
||||||
@ -220,14 +132,13 @@ export class PWCIntegrationService {
|
|||||||
// Try to extract from GSTIN (first 2 chars)
|
// Try to extract from GSTIN (first 2 chars)
|
||||||
if (dealerGst && dealerGst.length >= 2 && !isNaN(Number(dealerGst.substring(0, 2)))) {
|
if (dealerGst && dealerGst.length >= 2 && !isNaN(Number(dealerGst.substring(0, 2)))) {
|
||||||
dealerStateCode = dealerGst.substring(0, 2);
|
dealerStateCode = dealerGst.substring(0, 2);
|
||||||
} else if ((dealer as any).stateCode) {
|
} else if (dealer?.state) {
|
||||||
dealerStateCode = (dealer as any).stateCode;
|
// Approximate state code from state name or use 33 as default if it's RE state
|
||||||
|
dealerStateCode = dealer.state.toLowerCase().includes('tamil') ? "33" : "24";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch expenses if available (Moved to top)
|
|
||||||
// const expenses = await DealerCompletionExpense.findAll({ where: { requestId } });
|
|
||||||
|
|
||||||
let itemList: any[] = [];
|
let itemList: any[] = [];
|
||||||
|
let claimInvoiceItemsToCreate: any[] = [];
|
||||||
let totalAssAmt = 0;
|
let totalAssAmt = 0;
|
||||||
let totalIgstAmt = 0;
|
let totalIgstAmt = 0;
|
||||||
let totalCgstAmt = 0;
|
let totalCgstAmt = 0;
|
||||||
@ -235,6 +146,7 @@ export class PWCIntegrationService {
|
|||||||
let totalInvVal = 0;
|
let totalInvVal = 0;
|
||||||
|
|
||||||
const isIGST = dealerStateCode !== "33"; // If dealer state != Buyer state (33), it's IGST
|
const isIGST = dealerStateCode !== "33"; // If dealer state != Buyer state (33), it's IGST
|
||||||
|
const isNonGSTActivity = activity.taxationType === 'Non GST';
|
||||||
|
|
||||||
if (expenses && expenses.length > 0) {
|
if (expenses && expenses.length > 0) {
|
||||||
// Group expenses by HSN/SAC and GST Rate
|
// Group expenses by HSN/SAC and GST Rate
|
||||||
@ -252,11 +164,6 @@ export class PWCIntegrationService {
|
|||||||
const amount = Number(expense.amount) || 0;
|
const amount = Number(expense.amount) || 0;
|
||||||
const baseAmt = amount * qty;
|
const baseAmt = amount * qty;
|
||||||
|
|
||||||
const igst = isIGST ? (baseAmt * (gstRate / 100)) : 0;
|
|
||||||
const cgst = !isIGST ? (baseAmt * (gstRate / 200)) : 0;
|
|
||||||
const sgst = !isIGST ? (baseAmt * (gstRate / 200)) : 0;
|
|
||||||
const itemTotal = baseAmt + igst + cgst + sgst;
|
|
||||||
|
|
||||||
if (!groupedExpenses[groupKey]) {
|
if (!groupedExpenses[groupKey]) {
|
||||||
groupedExpenses[groupKey] = {
|
groupedExpenses[groupKey] = {
|
||||||
hsnCd,
|
hsnCd,
|
||||||
@ -266,9 +173,11 @@ export class PWCIntegrationService {
|
|||||||
igst: 0,
|
igst: 0,
|
||||||
cgst: 0,
|
cgst: 0,
|
||||||
sgst: 0,
|
sgst: 0,
|
||||||
|
utgst: 0,
|
||||||
itemTotal: 0,
|
itemTotal: 0,
|
||||||
description: expense.description || activity.title,
|
description: expense.description || activity.title,
|
||||||
expenseIds: [expense.expenseId]
|
expenseIds: [expense.expenseId],
|
||||||
|
hasUtgst: Number(expense.utgstRate || 0) > 0 || Number(expense.utgstAmt || 0) > 0
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const nextDesc = expense.description || activity.title;
|
const nextDesc = expense.description || activity.title;
|
||||||
@ -280,68 +189,98 @@ export class PWCIntegrationService {
|
|||||||
: updatedDesc;
|
: updatedDesc;
|
||||||
}
|
}
|
||||||
groupedExpenses[groupKey].expenseIds.push(expense.expenseId);
|
groupedExpenses[groupKey].expenseIds.push(expense.expenseId);
|
||||||
|
if (Number(expense.utgstRate || 0) > 0 || Number(expense.utgstAmt || 0) > 0) {
|
||||||
|
groupedExpenses[groupKey].hasUtgst = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
groupedExpenses[groupKey].baseAmt += baseAmt;
|
groupedExpenses[groupKey].baseAmt += baseAmt;
|
||||||
groupedExpenses[groupKey].igst += igst;
|
|
||||||
groupedExpenses[groupKey].cgst += cgst;
|
|
||||||
groupedExpenses[groupKey].sgst += sgst;
|
|
||||||
groupedExpenses[groupKey].itemTotal += itemTotal;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Persistence: Delete old items and insert new ones for this request
|
|
||||||
await ClaimInvoiceItem.destroy({ where: { requestId } });
|
|
||||||
|
|
||||||
itemList = Object.values(groupedExpenses).map((group: any, index: number) => {
|
itemList = Object.values(groupedExpenses).map((group: any, index: number) => {
|
||||||
|
// STRICT CALCULATION: Recalculate tax based on grouped baseAmt to satisfy PWC validation (Tax = Base * Rate)
|
||||||
|
const groupGstRate = Number(group.gstRate || 0);
|
||||||
|
const groupBaseAmt = Number(group.baseAmt || 0);
|
||||||
|
|
||||||
|
let calcIgst = 0, calcCgst = 0, calcSgst = 0, calcUtgst = 0;
|
||||||
|
let calcIgstRate = 0, calcCgstRate = 0, calcSgstRate = 0, calcUtgstRate = 0;
|
||||||
|
|
||||||
|
if (isIGST) {
|
||||||
|
calcIgst = Number((groupBaseAmt * groupGstRate / 100).toFixed(2));
|
||||||
|
calcIgstRate = groupGstRate;
|
||||||
|
} else {
|
||||||
|
const halfRate = groupGstRate / 2;
|
||||||
|
const halfTax = Number((groupBaseAmt * halfRate / 100).toFixed(2));
|
||||||
|
calcCgst = halfTax;
|
||||||
|
calcCgstRate = halfRate;
|
||||||
|
// Use UTGST if detected in any expense of this group, otherwise SGST
|
||||||
|
if (group.hasUtgst) {
|
||||||
|
calcUtgst = halfTax;
|
||||||
|
calcUtgstRate = halfRate;
|
||||||
|
} else {
|
||||||
|
calcSgst = halfTax;
|
||||||
|
calcSgstRate = halfRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const calcTotalInvVal = Number((groupBaseAmt + calcIgst + calcCgst + calcSgst + calcUtgst).toFixed(2));
|
||||||
|
|
||||||
// Accumulate overall totals
|
// Accumulate overall totals
|
||||||
totalAssAmt += group.baseAmt;
|
totalAssAmt += groupBaseAmt;
|
||||||
totalIgstAmt += group.igst;
|
totalIgstAmt += calcIgst;
|
||||||
totalCgstAmt += group.cgst;
|
totalCgstAmt += calcCgst;
|
||||||
totalSgstAmt += group.sgst;
|
totalSgstAmt += calcSgst + calcUtgst; // Sum of SGST and UTGST for summary
|
||||||
totalInvVal += group.itemTotal;
|
totalInvVal += calcTotalInvVal;
|
||||||
|
|
||||||
const slNo = index + 1;
|
const slNo = index + 1;
|
||||||
|
|
||||||
// Save to DB
|
|
||||||
const transactionCode = `${customInvoiceNumber}-${String(slNo).padStart(2, '0')}`;
|
const transactionCode = `${customInvoiceNumber}-${String(slNo).padStart(2, '0')}`;
|
||||||
|
|
||||||
ClaimInvoiceItem.create({
|
claimInvoiceItemsToCreate.push({
|
||||||
requestId,
|
requestId,
|
||||||
invoiceNumber: customInvoiceNumber,
|
invoiceNumber: customInvoiceNumber,
|
||||||
transactionCode: transactionCode,
|
transactionCode: transactionCode,
|
||||||
slNo: slNo,
|
slNo: slNo,
|
||||||
description: group.description,
|
description: group.description,
|
||||||
hsnCd: group.hsnCd, // Use actual HSN from group
|
hsnCd: group.hsnCd,
|
||||||
qty: 1,
|
qty: 1,
|
||||||
unit: "NOS",
|
unit: "NOS",
|
||||||
unitPrice: formatAmount(group.baseAmt),
|
unitPrice: formatAmount(groupBaseAmt),
|
||||||
assAmt: formatAmount(group.baseAmt),
|
assAmt: formatAmount(groupBaseAmt),
|
||||||
gstRt: formatRate(Number(group.gstRate)),
|
gstRt: formatRate(groupGstRate),
|
||||||
igstAmt: formatAmount(group.igst),
|
igstAmt: formatAmount(calcIgst),
|
||||||
cgstAmt: formatAmount(group.cgst),
|
cgstAmt: formatAmount(calcCgst),
|
||||||
sgstAmt: formatAmount(group.sgst),
|
sgstAmt: formatAmount(calcSgst),
|
||||||
totItemVal: formatAmount(group.itemTotal),
|
utgstAmt: formatAmount(calcUtgst),
|
||||||
isServc: group.isService ? "Y" : "N", // Use actual isService from group
|
igstRate: formatRate(calcIgstRate),
|
||||||
|
cgstRate: formatRate(calcCgstRate),
|
||||||
|
sgstRate: formatRate(calcSgstRate),
|
||||||
|
utgstRate: formatRate(calcUtgstRate),
|
||||||
|
totItemVal: formatAmount(calcTotalInvVal),
|
||||||
|
isServc: group.isService ? "Y" : "N",
|
||||||
expenseIds: group.expenseIds
|
expenseIds: group.expenseIds
|
||||||
}).catch((err: any) => logger.error(`[PWCIntegrationService] Error saving ClaimInvoiceItem:`, err));
|
});
|
||||||
|
|
||||||
|
// PWC Payload Item format
|
||||||
|
const hsnForPwc = isNonGSTActivity ? group.hsnCd : "87141090"; // Force valid HSN for PWC Payload if GST
|
||||||
|
const isServcForPwc = isNonGSTActivity ? (group.isService ? "Y" : "N") : "N"; // 87141090 is Goods
|
||||||
|
|
||||||
return {
|
return {
|
||||||
SlNo: String(slNo),
|
SlNo: String(slNo),
|
||||||
PrdNm: group.description,
|
PrdNm: group.description,
|
||||||
PrdDesc: group.description,
|
PrdDesc: group.description,
|
||||||
HsnCd: "87141090", // Known working HSN from old invoice
|
HsnCd: hsnForPwc,
|
||||||
IsServc: "N", // 87141090 is Goods
|
IsServc: isServcForPwc,
|
||||||
Qty: formatQty(1),
|
Qty: formatQty(1),
|
||||||
Unit: "NOS",
|
Unit: "NOS",
|
||||||
UnitPrice: formatAmount(group.baseAmt),
|
UnitPrice: formatAmount(groupBaseAmt),
|
||||||
TotAmt: formatAmount(group.baseAmt),
|
TotAmt: formatAmount(groupBaseAmt),
|
||||||
Discount: 0,
|
Discount: 0,
|
||||||
PreTaxVal: formatAmount(group.baseAmt),
|
PreTaxVal: formatAmount(groupBaseAmt),
|
||||||
AssAmt: formatAmount(group.baseAmt),
|
AssAmt: formatAmount(groupBaseAmt),
|
||||||
GstRt: formatRate(Number(group.gstRate)),
|
GstRt: formatRate(groupGstRate),
|
||||||
IgstAmt: formatAmount(group.igst),
|
IgstAmt: formatAmount(calcIgst),
|
||||||
CgstAmt: formatAmount(group.cgst),
|
CgstAmt: formatAmount(calcCgst),
|
||||||
SgstAmt: formatAmount(group.sgst),
|
SgstAmt: formatAmount(calcSgst + calcUtgst),
|
||||||
CesRt: 0,
|
CesRt: 0,
|
||||||
CesAmt: 0,
|
CesAmt: 0,
|
||||||
CesNonAdValAmt: 0,
|
CesNonAdValAmt: 0,
|
||||||
@ -349,17 +288,22 @@ export class PWCIntegrationService {
|
|||||||
StateCesAmt: 0,
|
StateCesAmt: 0,
|
||||||
StateCesNonAdValAmt: 0,
|
StateCesNonAdValAmt: 0,
|
||||||
OthChrg: 0,
|
OthChrg: 0,
|
||||||
TotItemVal: formatAmount(group.itemTotal)
|
TotItemVal: formatAmount(calcTotalInvVal)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Fallback to single line item if no expenses found
|
// Fallback to single line item if no expenses found
|
||||||
const gstRate = Number(activity.gstRate || 18);
|
const gstRate = isNonGSTActivity ? 0 : Number(activity.gstRate || 18);
|
||||||
const assAmt = finalAmount;
|
const assAmt = finalAmount;
|
||||||
const igstAmt = isIGST ? (finalAmount * (gstRate / 100)) : 0;
|
let igstAmt = 0, cgstAmt = 0, sgstAmt = 0;
|
||||||
const cgstAmt = !isIGST ? (finalAmount * (gstRate / 200)) : 0;
|
|
||||||
const sgstAmt = !isIGST ? (finalAmount * (gstRate / 200)) : 0;
|
if (!isNonGSTActivity) {
|
||||||
const totalTax = igstAmt + cgstAmt + sgstAmt;
|
igstAmt = isIGST ? (finalAmount * (gstRate / 100)) : 0;
|
||||||
|
cgstAmt = !isIGST ? (finalAmount * (gstRate / 200)) : 0;
|
||||||
|
sgstAmt = !isIGST ? (finalAmount * (gstRate / 200)) : 0;
|
||||||
|
}
|
||||||
|
const utgstAmt = 0; // Fallback assumes SGST for simplicity unless we detect state
|
||||||
|
const totalTax = igstAmt + cgstAmt + sgstAmt + utgstAmt;
|
||||||
const totalItemVal = finalAmount + totalTax;
|
const totalItemVal = finalAmount + totalTax;
|
||||||
|
|
||||||
totalAssAmt = assAmt;
|
totalAssAmt = assAmt;
|
||||||
@ -370,14 +314,11 @@ export class PWCIntegrationService {
|
|||||||
|
|
||||||
const slNo = 1;
|
const slNo = 1;
|
||||||
|
|
||||||
// Persistence: Delete old items and insert single fallback item
|
|
||||||
await ClaimInvoiceItem.destroy({ where: { requestId } });
|
|
||||||
|
|
||||||
const fallbackHsn = activity.hsnCode || activity.sacCode || "998311";
|
const fallbackHsn = activity.hsnCode || activity.sacCode || "998311";
|
||||||
const fallbackIsService = this.isServiceHSN(fallbackHsn) === "Y";
|
const fallbackIsService = this.isServiceHSN(fallbackHsn) === "Y";
|
||||||
const transactionCode = `${customInvoiceNumber}-${String(slNo).padStart(2, '0')}`;
|
const transactionCode = `${customInvoiceNumber}-${String(slNo).padStart(2, '0')}`;
|
||||||
|
|
||||||
ClaimInvoiceItem.create({
|
claimInvoiceItemsToCreate.push({
|
||||||
requestId,
|
requestId,
|
||||||
invoiceNumber: customInvoiceNumber,
|
invoiceNumber: customInvoiceNumber,
|
||||||
transactionCode: transactionCode,
|
transactionCode: transactionCode,
|
||||||
@ -392,19 +333,22 @@ export class PWCIntegrationService {
|
|||||||
igstAmt: formatAmount(igstAmt),
|
igstAmt: formatAmount(igstAmt),
|
||||||
cgstAmt: formatAmount(cgstAmt),
|
cgstAmt: formatAmount(cgstAmt),
|
||||||
sgstAmt: formatAmount(sgstAmt),
|
sgstAmt: formatAmount(sgstAmt),
|
||||||
|
utgstAmt: 0,
|
||||||
totItemVal: formatAmount(totalItemVal),
|
totItemVal: formatAmount(totalItemVal),
|
||||||
isServc: fallbackIsService ? "Y" : "N",
|
isServc: fallbackIsService ? "Y" : "N",
|
||||||
expenseIds: []
|
expenseIds: []
|
||||||
}).catch((err: any) => logger.error(`[PWCIntegrationService] Error saving ClaimInvoiceItem (fallback):`, err));
|
});
|
||||||
|
|
||||||
const hsnCd = "87141090"; // Force valid HSN for PWC Payload
|
// PWC Payload Item format
|
||||||
|
const hsnForPwc = isNonGSTActivity ? fallbackHsn : "87141090"; // Force valid HSN for PWC Payload if GST
|
||||||
|
const isServcForPwc = isNonGSTActivity ? (fallbackIsService ? "Y" : "N") : "N"; // 87141090 is Goods
|
||||||
|
|
||||||
itemList = [{
|
itemList = [{
|
||||||
SlNo: String(slNo),
|
SlNo: String(slNo),
|
||||||
PrdNm: activity.title,
|
PrdNm: activity.title,
|
||||||
PrdDesc: activity.title,
|
PrdDesc: activity.title,
|
||||||
HsnCd: hsnCd,
|
HsnCd: hsnForPwc,
|
||||||
IsServc: "N",
|
IsServc: isServcForPwc,
|
||||||
Qty: formatQty(1),
|
Qty: formatQty(1),
|
||||||
Unit: "NOS",
|
Unit: "NOS",
|
||||||
UnitPrice: formatAmount(assAmt),
|
UnitPrice: formatAmount(assAmt),
|
||||||
@ -427,6 +371,33 @@ export class PWCIntegrationService {
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEW LOGIC: Check for Non-GST Activity
|
||||||
|
if (isNonGSTActivity) {
|
||||||
|
logger.info(`[PWC] Activity ${activity.title} is Non-GST. Skipping IRN generation.`);
|
||||||
|
|
||||||
|
// Persistence for Non-GST (Awaited)
|
||||||
|
await ClaimInvoiceItem.destroy({ where: { requestId } });
|
||||||
|
if (claimInvoiceItemsToCreate.length > 0) {
|
||||||
|
await ClaimInvoiceItem.bulkCreate(claimInvoiceItemsToCreate);
|
||||||
|
logger.info(`[PWCIntegration] Persisted ${claimInvoiceItemsToCreate.length} line items for Non-GST request ${requestId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return internal success response (No IRN)
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
irn: undefined,
|
||||||
|
ackNo: undefined,
|
||||||
|
ackDate: new Date(),
|
||||||
|
signedInvoice: undefined,
|
||||||
|
qrCode: undefined, // No QR for Non-GST
|
||||||
|
qrImage: undefined,
|
||||||
|
totalIgstAmt: formatAmount(totalIgstAmt),
|
||||||
|
totalCgstAmt: formatAmount(totalCgstAmt),
|
||||||
|
totalSgstAmt: formatAmount(totalSgstAmt),
|
||||||
|
totalAssAmt: formatAmount(totalAssAmt)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Construct PWC Payload - Aligned with sample format provided by user
|
// Construct PWC Payload - Aligned with sample format provided by user
|
||||||
const payload = [
|
const payload = [
|
||||||
{
|
{
|
||||||
@ -456,16 +427,16 @@ export class PWCIntegrationService {
|
|||||||
},
|
},
|
||||||
SellerDtls: {
|
SellerDtls: {
|
||||||
Gstin: dealerGst,
|
Gstin: dealerGst,
|
||||||
LglNm: (dealer as any).dealership || 'Dealer',
|
LglNm: dealer?.dealerName || 'Dealer',
|
||||||
TrdNm: (dealer as any).dealership || 'Dealer',
|
TrdNm: dealer?.dealerName || 'Dealer',
|
||||||
Addr1: (dealer as any).showroomAddress || "Address Line 1",
|
Addr1: dealer?.city || "Address Line 1",
|
||||||
Loc: (dealer as any).location || "Location",
|
Loc: dealer?.city || "Location",
|
||||||
Pin: (dealerGst === validQaGst && String((dealer as any).showroomPincode || '').substring(0, 1) !== '3')
|
Pin: (dealerGst === validQaGst)
|
||||||
? 380001
|
? 380001
|
||||||
: Number((dealer as any).showroomPincode) || 600001,
|
: 600001,
|
||||||
Stcd: dealerStateCode,
|
Stcd: dealerStateCode,
|
||||||
Ph: (dealer as any).dpContactNumber || "9998887776",
|
Ph: dealer?.phone || "9998887776",
|
||||||
Em: (dealer as any).dealerPrincipalEmailId || "Supplier@inv.com"
|
Em: dealer?.email || "Supplier@inv.com"
|
||||||
},
|
},
|
||||||
BuyerDtls: {
|
BuyerDtls: {
|
||||||
Gstin: "33AAACE3882D1ZZ", // Royal Enfield GST (Tamil Nadu)
|
Gstin: "33AAACE3882D1ZZ", // Royal Enfield GST (Tamil Nadu)
|
||||||
@ -482,7 +453,7 @@ export class PWCIntegrationService {
|
|||||||
AssVal: formatAmount(totalAssAmt),
|
AssVal: formatAmount(totalAssAmt),
|
||||||
IgstVal: formatAmount(totalIgstAmt),
|
IgstVal: formatAmount(totalIgstAmt),
|
||||||
CgstVal: formatAmount(totalCgstAmt),
|
CgstVal: formatAmount(totalCgstAmt),
|
||||||
SgstVal: formatAmount(totalSgstAmt),
|
SgstVal: formatAmount(totalSgstAmt), // Sum of SGST and UTGST for summary
|
||||||
TotInvVal: formatAmount(totalInvVal)
|
TotInvVal: formatAmount(totalInvVal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -544,6 +515,19 @@ export class PWCIntegrationService {
|
|||||||
return { success: false, error: errorMessage };
|
return { success: false, error: errorMessage };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Persistence for GST (Awaited) - Only if IRN generation was successful
|
||||||
|
await ClaimInvoiceItem.destroy({ where: { requestId } });
|
||||||
|
if (claimInvoiceItemsToCreate.length > 0) {
|
||||||
|
await ClaimInvoiceItem.bulkCreate(claimInvoiceItemsToCreate);
|
||||||
|
logger.info(`[PWCIntegration] Persisted ${claimInvoiceItemsToCreate.length} line items for GST request ${requestId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update invoice with dealer GSTIN if available
|
||||||
|
if (dealer?.gstin) {
|
||||||
|
await ClaimInvoice.update({ consignorGsin: dealer.gstin }, { where: { requestId } })
|
||||||
|
.catch(err => logger.error(`[PWCIntegration] Error updating invoice consignorGsin:`, err));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
irn,
|
irn,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user