Re_Backend/src/scripts/seed-demo-requests.ts

248 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Seed demo data: Form 16 submissions and other workflow requests.
* Data is inserted into database tables only (no hardcoded data in app code).
*
* Prerequisites:
* - Run seed:admin-user (for admin user)
* - Run seed:dealer-user (for dealer user + dealer record for Form 16)
*
* Usage: npm run seed:demo-requests
*/
import { Op } from 'sequelize';
import { sequelize } from '../config/database';
import {
User,
WorkflowRequest,
Form16aSubmission,
Form16CreditNote,
Activity,
Document,
Dealer,
} from '../models';
import { Priority, WorkflowStatus } from '../types/common.types';
import { generateRequestNumber } from '../utils/helpers';
import logger from '../utils/logger';
const DEMO_MARKER_TITLE_PREFIX = 'Form 16A - 2024-25'; // Used to detect existing demo Form 16 data
const DEMO_CUSTOM_TITLE = '[Demo] Sample workflow request';
async function getOrResolveUsers(): Promise<{ adminUser: User | null; dealerUser: User | null; dealerCode: string | null }> {
const adminUser = await User.findOne({ where: { email: 'admin@example.com' } });
const dealerUser = await User.findOne({ where: { email: 'testreflow@example.com' } });
let dealerCode: string | null = null;
if (dealerUser) {
const dealer = await Dealer.findOne({
where: { dealerPrincipalEmailId: 'testreflow@example.com', isActive: true },
attributes: ['salesCode', 'dlrcode'],
});
dealerCode = dealer?.salesCode ?? dealer?.dlrcode ?? null;
}
return { adminUser, dealerUser, dealerCode };
}
async function hasExistingDemoForm16(): Promise<boolean> {
const count = await WorkflowRequest.count({
where: {
templateType: 'FORM_16',
title: { [Op.like]: `${DEMO_MARKER_TITLE_PREFIX}%` },
},
});
return count >= 2;
}
/** Demo dealers used for "non-submitted dealers" list: active dealers with no Form 16 submission for the demo FY/quarter. */
const DEMO_NON_SUBMITTED_DEALERS = [
{ dlrcode: 'DEMO-NOSUB-001', dealership: 'Demo Motors Mumbai', dealerPrincipalName: 'Demo Mumbai', dealerPrincipalEmailId: 'demo.nosub.1@example.com', state: 'Maharashtra', city: 'Mumbai' },
{ dlrcode: 'DEMO-NOSUB-002', dealership: 'Demo Enfield Delhi', dealerPrincipalName: 'Demo Delhi', dealerPrincipalEmailId: 'demo.nosub.2@example.com', state: 'Delhi', city: 'New Delhi' },
{ dlrcode: 'DEMO-NOSUB-003', dealership: 'Demo Royal Bangalore', dealerPrincipalName: 'Demo Bangalore', dealerPrincipalEmailId: 'demo.nosub.3@example.com', state: 'Karnataka', city: 'Bengaluru' },
];
async function ensureDemoNonSubmittedDealers(): Promise<void> {
for (const data of DEMO_NON_SUBMITTED_DEALERS) {
const [dealer] = await Dealer.findOrCreate({
where: { dlrcode: data.dlrcode },
defaults: {
...data,
salesCode: data.dlrcode,
isActive: true,
} as any,
});
if (dealer && !dealer.isActive) {
await dealer.update({ isActive: true });
}
}
logger.info('[Seed Demo] Demo non-submitted dealers ensured (they have no Form 16 submissions for 2024-25).');
}
async function seedDemoRequests(): Promise<void> {
// Always ensure demo dealers exist first (for both submission and non-submitted lists).
// These dealers are in the dealers table; only the test dealer gets Form 16 submissions below.
await ensureDemoNonSubmittedDealers();
const { adminUser, dealerUser, dealerCode } = await getOrResolveUsers();
const initiatorId = adminUser?.userId ?? dealerUser?.userId;
if (!initiatorId) {
logger.warn('[Seed Demo] No admin or dealer user found. Run seed:admin-user and seed:dealer-user first.');
return;
}
if (await hasExistingDemoForm16()) {
logger.info('[Seed Demo] Demo Form 16 requests already present. Skipping to avoid duplicates.');
return;
}
const now = new Date();
// ---- 1) Create a few CUSTOM (nonForm 16) requests so "other requests" are visible ----
for (let i = 1; i <= 2; i++) {
const requestNumber = await generateRequestNumber();
await WorkflowRequest.create({
requestNumber,
initiatorId,
templateType: 'CUSTOM',
workflowType: 'NON_TEMPLATIZED',
title: `${DEMO_CUSTOM_TITLE} ${i}`,
description: `Demo workflow request for testing. Created by seed script.`,
priority: Priority.STANDARD,
status: i === 1 ? WorkflowStatus.PENDING : WorkflowStatus.PENDING,
currentLevel: 1,
totalLevels: 1,
totalTatHours: 48,
submissionDate: now,
isDraft: false,
isDeleted: false,
isPaused: false,
});
}
// ---- 2) Create Form 16 requests + submissions (and optional credit notes / activity / docs) ----
if (!dealerUser || !dealerCode) {
logger.warn('[Seed Demo] Dealer user or dealer code missing. Form 16 demo requests skipped. Run seed:dealer-user.');
} else {
const form16InitiatorId = dealerUser.userId;
const form16Demos = [
{ fy: '2024-25', quarter: 'Q1', deductor: 'Royal Enfield Motors Ltd', tds: 15000, total: 150000, cnAmount: 15000, withCreditNote: true, withdrawn: false },
{ fy: '2024-25', quarter: 'Q2', deductor: 'Royal Enfield Motors Ltd', tds: 18000, total: 180000, cnAmount: 18000, withCreditNote: true, withdrawn: true },
{ fy: '2024-25', quarter: 'Q3', deductor: 'Royal Enfield Motors Ltd', tds: 12000, total: 120000, cnAmount: null, withCreditNote: false, withdrawn: false },
];
for (const demo of form16Demos) {
const requestNumber = await generateRequestNumber();
const title = `Form 16A - ${demo.fy} ${demo.quarter}`;
const description = `Form 16A TDS certificate submission. Deductor: ${demo.deductor}.`;
const workflow = await WorkflowRequest.create({
requestNumber,
initiatorId: form16InitiatorId,
templateType: 'FORM_16',
workflowType: 'FORM_16',
title,
description,
priority: Priority.STANDARD,
status: demo.withCreditNote && !demo.withdrawn ? WorkflowStatus.CLOSED : WorkflowStatus.PENDING,
currentLevel: 1,
totalLevels: 1,
totalTatHours: 0,
submissionDate: now,
closureDate: demo.withCreditNote && !demo.withdrawn ? now : undefined,
isDraft: false,
isDeleted: false,
isPaused: false,
});
const requestId = (workflow as any).requestId;
const form16aNumber = `DEMO-F16-${demo.quarter}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
const submission = await Form16aSubmission.create({
requestId,
dealerCode,
form16aNumber,
financialYear: demo.fy,
quarter: demo.quarter,
version: 1,
tdsAmount: demo.tds,
totalAmount: demo.total,
tanNumber: 'BLRE00001E',
deductorName: demo.deductor,
documentUrl: 'https://example.com/demo-form16a.pdf',
status: 'pending',
submittedDate: now,
});
// Activity: dealer submitted Form 16
await Activity.create({
requestId,
userId: form16InitiatorId,
userName: (dealerUser as any).displayName || (dealerUser as any).firstName + ' ' + (dealerUser as any).lastName || 'Dealer',
activityType: 'FORM_16_SUBMITTED',
activityDescription: `Dealer submitted Form 16A for ${demo.fy} ${demo.quarter}. Certificate: ${form16aNumber}.`,
activityCategory: 'submission',
isSystemEvent: false,
});
// Document: attached Form 16 (placeholder so Docs tab shows an entry)
await Document.create({
requestId,
uploadedBy: form16InitiatorId,
fileName: `form16a-${demo.quarter}.pdf`,
originalFileName: `Form16A_${demo.fy}_${demo.quarter}.pdf`,
fileType: 'application/pdf',
fileExtension: 'pdf',
fileSize: 1024,
filePath: `form16-demo/${requestId}/form16a.pdf`,
mimeType: 'application/pdf',
checksum: 'demo-checksum-' + requestId.slice(0, 8),
isGoogleDoc: false,
category: 'SUPPORTING',
version: 1,
isDeleted: false,
downloadCount: 0,
uploadedAt: now,
});
if (demo.withCreditNote && demo.cnAmount != null) {
const creditNote = await Form16CreditNote.create({
submissionId: submission.id,
creditNoteNumber: `DEMO-CN-${demo.quarter}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
sapDocumentNumber: `SAP-${demo.quarter}-DEMO`,
amount: demo.cnAmount,
issueDate: now.toISOString().slice(0, 10) as unknown as Date,
financialYear: demo.fy,
quarter: demo.quarter,
status: demo.withdrawn ? 'withdrawn' : 'issued',
issuedBy: adminUser?.userId,
createdAt: now,
updatedAt: now,
});
await Activity.create({
requestId,
userId: adminUser?.userId ?? undefined,
userName: 'RE Admin',
activityType: demo.withdrawn ? 'CREDIT_NOTE_WITHDRAWN' : 'CREDIT_NOTE_ISSUED',
activityDescription: demo.withdrawn
? `Credit note ${creditNote.creditNoteNumber} was withdrawn by RE user.`
: `Credit note ${creditNote.creditNoteNumber} generated for ${demo.fy} ${demo.quarter}.`,
activityCategory: 'workflow',
isSystemEvent: false,
});
}
}
}
logger.info('[Seed Demo] Demo requests, Form 16 submissions, and non-submitted dealers demo data created successfully.');
}
if (require.main === module) {
sequelize
.authenticate()
.then(() => seedDemoRequests())
.then(() => process.exit(0))
.catch((err) => {
logger.error('[Seed Demo] Failed:', err);
process.exit(1);
});
}
export { seedDemoRequests, ensureDemoNonSubmittedDealers };