multi iteration flow added and the external dealer api aded

This commit is contained in:
laxmanhalaki 2026-03-03 18:09:26 +05:30
parent 5be1e319b0
commit 7488ae3bee
10 changed files with 195 additions and 157 deletions

View File

@ -1 +1 @@
import{a as s}from"./index-BCZm9H2Q.js";import"./radix-vendor-CYvDqP9X.js";import"./charts-vendor-BVfwAPj-.js";import"./utils-vendor-BTBPSQfW.js";import"./ui-vendor-CX5oLBI_.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-B_rK4TXr.js";async function m(n){return(await s.post(`/conclusions/${n}/generate`)).data.data}async function f(n,t){return(await s.post(`/conclusions/${n}/finalize`,{finalRemark:t})).data.data}async function d(n){var t;try{return(await s.get(`/conclusions/${n}`)).data.data}catch(o){if(((t=o.response)==null?void 0:t.status)===404)return null;throw o}}export{f as finalizeConclusion,m as generateConclusion,d as getConclusion};
import{a as s}from"./index-yOqi1S1C.js";import"./radix-vendor-CYvDqP9X.js";import"./charts-vendor-BVfwAPj-.js";import"./utils-vendor-BTBPSQfW.js";import"./ui-vendor-CX5oLBI_.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-B_rK4TXr.js";async function m(n){return(await s.post(`/conclusions/${n}/generate`)).data.data}async function f(n,t){return(await s.post(`/conclusions/${n}/finalize`,{finalRemark:t})).data.data}async function d(n){var t;try{return(await s.get(`/conclusions/${n}`)).data.data}catch(o){if(((t=o.response)==null?void 0:t.status)===404)return null;throw o}}export{f as finalizeConclusion,m as generateConclusion,d as getConclusion};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,7 @@
<!-- Preload essential fonts and icons -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<script type="module" crossorigin src="/assets/index-BCZm9H2Q.js"></script>
<script type="module" crossorigin src="/assets/index-yOqi1S1C.js"></script>
<link rel="modulepreload" crossorigin href="/assets/charts-vendor-BVfwAPj-.js">
<link rel="modulepreload" crossorigin href="/assets/radix-vendor-CYvDqP9X.js">
<link rel="modulepreload" crossorigin href="/assets/utils-vendor-BTBPSQfW.js">
@ -21,7 +21,7 @@
<link rel="modulepreload" crossorigin href="/assets/socket-vendor-TjCxX7sJ.js">
<link rel="modulepreload" crossorigin href="/assets/redux-vendor-tbZCm13o.js">
<link rel="modulepreload" crossorigin href="/assets/router-vendor-B_rK4TXr.js">
<link rel="stylesheet" crossorigin href="/assets/index-DjE6S9VF.css">
<link rel="stylesheet" crossorigin href="/assets/index-XBJXaMj2.css">
</head>
<body>

View File

@ -1183,10 +1183,18 @@ export class DealerClaimService {
hsnCode: item.hsnCode || '',
gstRate: Number(item.gstRate) || 0,
gstAmt: Number(item.gstAmt) || 0,
cgstRate: Number(item.cgstRate) || 0,
cgstAmt: Number(item.cgstAmt) || 0,
sgstRate: Number(item.sgstRate) || 0,
sgstAmt: Number(item.sgstAmt) || 0,
igstRate: Number(item.igstRate) || 0,
igstAmt: Number(item.igstAmt) || 0,
totalAmt: Number(item.totalAmt) || 0
utgstRate: Number(item.utgstRate) || 0,
utgstAmt: Number(item.utgstAmt) || 0,
cessRate: Number(item.cessRate) || 0,
cessAmt: Number(item.cessAmt) || 0,
totalAmt: Number(item.totalAmt) || 0,
isService: !!item.isService
}));
}
// Note: costBreakup JSONB field has been removed - only using separate table now
@ -1268,6 +1276,7 @@ export class DealerClaimService {
proposalDetails: transformedProposalDetails,
completionDetails: serializedCompletionDetails,
internalOrder: serializedInternalOrder,
internalOrders: serializedInternalOrders, // Return full list for UI
// New normalized tables
budgetTracking: serializedBudgetTracking,
invoice: serializedInvoice,
@ -1766,11 +1775,35 @@ export class DealerClaimService {
// If blocking amount is 0 but ioNumber is provided, just save the IO details without blocking
if (blockedAmount <= 0) {
// Allow saving IO details (ioNumber only) even without blocking amount
// This is useful when Requestor Evaluation is in progress but amount hasn't been blocked yet
// This is useful when Step 3/Requestor Evaluation is in progress but amount hasn't been blocked yet or for linking IO
if (ioData.ioNumber) {
const organizedBy = organizedByUserId || null;
// Always create a new Internal Order record for each block/provision (supporting multiple IOs)
// Check if an IO record already exists for this request and IO number
// This prevents duplicate 0-amount "provisioned" records when re-saving IO details
const existingIO = await InternalOrder.findOne({
where: {
requestId,
ioNumber: ioData.ioNumber
}
});
if (existingIO) {
// Update existing record with latest remark and organizer info if provided
await existingIO.update({
ioRemark: ioData.ioRemark || existingIO.ioRemark || '',
organizedBy: organizedBy || existingIO.organizedBy || undefined,
organizedAt: new Date(),
});
logger.info(`[DealerClaimService] Existing IO record updated for request: ${requestId}`, {
ioNumber: ioData.ioNumber,
status: existingIO.status
});
return;
}
// Create a new Internal Order record if none exists for this IO and request
await InternalOrder.create({
requestId,
ioNumber: ioData.ioNumber,
@ -1915,15 +1948,6 @@ export class DealerClaimService {
}
}
// Update budget tracking with blocked amount FIRST
await ClaimBudgetTracking.upsert({
requestId,
ioBlockedAmount: finalBlockedAmount,
ioBlockedAt: new Date(),
budgetStatus: BudgetStatus.BLOCKED,
currency: 'INR',
});
// Save IO history AFTER budget tracking update succeeds (only if ioLevel exists)
if (ioLevel && ioHistoryUserId) {
try {
@ -2576,6 +2600,22 @@ export class DealerClaimService {
costItems: costItems.map(i => ({
description: i.itemDescription,
amount: Number(i.amount || 0),
quantity: Number(i.quantity || 1),
hsnCode: i.hsnCode || '',
gstRate: Number(i.gstRate || 0),
gstAmt: Number(i.gstAmt || 0),
cgstRate: Number(i.cgstRate || 0),
cgstAmt: Number(i.cgstAmt || 0),
sgstRate: Number(i.sgstRate || 0),
sgstAmt: Number(i.sgstAmt || 0),
igstRate: Number(i.igstRate || 0),
igstAmt: Number(i.igstAmt || 0),
utgstRate: Number(i.utgstRate || 0),
utgstAmt: Number(i.utgstAmt || 0),
cessRate: Number(i.cessRate || 0),
cessAmt: Number(i.cessAmt || 0),
totalAmt: Number(i.totalAmt || 0),
isService: !!i.isService,
order: i.itemOrder
})),
otherDocuments: supportingDocs.map(doc => ({

View File

@ -284,84 +284,84 @@ export class DealerClaimApprovalService {
// Fallback: proceed to Step 4 normally if history check fails
}
}
} else {
logger.info(`[DealerClaimApproval] No next level found after level ${currentLevelNumber} - this may be the final approval`);
}
if (nextLevel) {
// Check if next level is paused - if so, don't activate it
if ((nextLevel as any).isPaused || (nextLevel as any).status === 'PAUSED') {
logger.warn(`[DealerClaimApproval] Cannot activate next level ${nextLevelNumber} - level is paused`);
throw new Error('Cannot activate next level - the next approval level is currently paused. Please resume it first.');
}
// Important: Update nextLevelNumber in case nextLevel was shifted (e.g. Step 4 skip)
// This ensures WorkflowRequest.currentLevel is updated to the correct active level
const finalNextLevelNumber = nextLevel ? (nextLevel.levelNumber || 0) : null;
// Activate next level
await nextLevel.update({
status: ApprovalStatus.IN_PROGRESS,
levelStartTime: now,
tatStartTime: now
});
// Schedule TAT jobs for the next level
try {
const workflowPriority = (wf as any)?.priority || 'STANDARD';
await tatSchedulerService.scheduleTatJobs(
level.requestId,
(nextLevel as any).levelId,
(nextLevel as any).approverId,
Number((nextLevel as any).tatHours),
now,
workflowPriority
);
logger.info(`[DealerClaimApproval] TAT jobs scheduled for next level ${nextLevelNumber} (Priority: ${workflowPriority})`);
} catch (tatError) {
logger.error(`[DealerClaimApproval] Failed to schedule TAT jobs for next level:`, tatError);
// Don't fail the approval if TAT scheduling fails
}
// Update workflow current level
if (nextLevelNumber !== null) {
await WorkflowRequest.update(
{ currentLevel: nextLevelNumber },
{ where: { requestId: level.requestId } }
);
// Update the APPROVE snapshot's changeReason to include movement information
// This ensures the approval snapshot shows both the approval and the movement
// We don't create a separate WORKFLOW snapshot for approvals - only APPROVE snapshot
try {
const { DealerClaimHistory } = await import('@models/DealerClaimHistory');
const { SnapshotType } = await import('@models/DealerClaimHistory');
const approvalHistory = await DealerClaimHistory.findOne({
where: {
requestId: level.requestId,
approvalLevelId: level.levelId,
snapshotType: SnapshotType.APPROVE
},
order: [['createdAt', 'DESC']]
});
if (approvalHistory) {
// Use the robust approvalComment from outer scope
const updatedChangeReason = approvalComment
? `Approved by ${level.approverName || level.approverEmail}, moved to next level (${nextLevelNumber}). Comment: ${approvalComment}`
: `Approved by ${level.approverName || level.approverEmail}, moved to next level (${nextLevelNumber})`;
await approvalHistory.update({
changeReason: updatedChangeReason
});
}
} catch (updateError) {
// Log error but don't fail - this is just updating the changeReason for better display
logger.warn(`[DealerClaimApproval] Failed to update approval history changeReason (non-critical):`, updateError);
if (nextLevel) {
// Check if next level is paused - if so, don't activate it
if ((nextLevel as any).isPaused || (nextLevel as any).status === 'PAUSED') {
logger.warn(`[DealerClaimApproval] Cannot activate next level ${finalNextLevelNumber} - level is paused`);
throw new Error('Cannot activate next level - the next approval level is currently paused. Please resume it first.');
}
logger.info(`[DealerClaimApproval] Approved level ${level.levelNumber}. Activated next level ${nextLevelNumber} for workflow ${level.requestId}`);
}
// Activate next level
await nextLevel.update({
status: ApprovalStatus.IN_PROGRESS,
levelStartTime: now,
tatStartTime: now
});
// Handle dealer claim-specific step processing
// Schedule TAT jobs for the next level
try {
const workflowPriority = (wf as any)?.priority || 'STANDARD';
await tatSchedulerService.scheduleTatJobs(
level.requestId,
(nextLevel as any).levelId,
(nextLevel as any).approverId,
Number((nextLevel as any).tatHours),
now,
workflowPriority
);
logger.info(`[DealerClaimApproval] TAT jobs scheduled for next level ${finalNextLevelNumber} (Priority: ${workflowPriority})`);
} catch (tatError) {
logger.error(`[DealerClaimApproval] Failed to schedule TAT jobs for next level:`, tatError);
// Don't fail the approval if TAT scheduling fails
}
// Update workflow current level
if (finalNextLevelNumber !== null) {
await WorkflowRequest.update(
{ currentLevel: finalNextLevelNumber },
{ where: { requestId: level.requestId } }
);
// Update the APPROVE snapshot's changeReason to include movement information
// This ensures the approval snapshot shows both the approval and the movement
// We don't create a separate WORKFLOW snapshot for approvals - only APPROVE snapshot
try {
const { DealerClaimHistory } = await import('@models/DealerClaimHistory');
const { SnapshotType } = await import('@models/DealerClaimHistory');
const approvalHistory = await DealerClaimHistory.findOne({
where: {
requestId: level.requestId,
approvalLevelId: level.levelId,
snapshotType: SnapshotType.APPROVE
},
order: [['createdAt', 'DESC']]
});
if (approvalHistory) {
// Use the robust approvalComment from outer scope
const updatedChangeReason = approvalComment
? `Approved by ${level.approverName || level.approverEmail}, moved to next level (${finalNextLevelNumber}). Comment: ${approvalComment}`
: `Approved by ${level.approverName || level.approverEmail}, moved to next level (${finalNextLevelNumber})`;
await approvalHistory.update({
changeReason: updatedChangeReason
});
}
} catch (updateError) {
// Log error but don't fail - this is just updating the changeReason for better display
logger.warn(`[DealerClaimApproval] Failed to update approval history changeReason (non-critical):`, updateError);
}
logger.info(`[DealerClaimApproval] Approved level ${level.levelNumber}. Activated next level ${finalNextLevelNumber} for workflow ${level.requestId}`);
}
} // Handle dealer claim-specific step processing
const currentLevelName = (level.levelName || '').toLowerCase();
// Check by levelName first, use levelNumber only as fallback if levelName is missing
// This handles cases where additional approvers shift step numbers

View File

@ -114,12 +114,10 @@ export class PWCIntegrationService {
// Extract State Code from Dealer GSTIN
let dealerGst = dealer?.gstin;
const uatGst = '24AAAPI3182M002';
const isDevOrUat = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'uat';
if (isDevOrUat) {
logger.info(`[PWC] Using Dev/UAT authorized GSTIN replacement: ${uatGst} (Original: ${dealerGst || 'empty'})`);
dealerGst = uatGst;
logger.info(`[PWC] Running in ${process.env.NODE_ENV} mode. Original Dealer GST: ${dealerGst || 'empty'}`);
}
logger.info(`[PWC] Final GSTIN being used for authentication and seller: ${dealerGst}`);
@ -434,7 +432,7 @@ export class PWCIntegrationService {
TrdNm: dealer?.dealerName || 'Dealer',
Addr1: dealer?.city || "Address Line 1",
Loc: dealer?.city || "Location",
Pin: Number(dealer?.pincode || (dealerGst === uatGst ? 380001 : 600001)),
Pin: Number(dealer?.pincode || 600001),
Stcd: dealerStateCode,
Ph: dealer?.phone || "9998887776",
Em: dealer?.email || "Supplier@inv.com"

Binary file not shown.