All files / src/domains/mdm/application/services master-data-import.service.ts

0% Statements 0/33
0% Branches 0/38
0% Functions 0/3
0% Lines 0/28

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98                                                                                                                                                                                                   
import {
  Injectable,
  Logger,
  BadRequestException,
  NotFoundException,
} from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model, Types } from 'mongoose';
import {
  MdmImportJob,
  MdmImportRow,
  MdmImportMapping,
} from '../../infrastructure/schemas/import-export.schema';
import { MasterDataRecordService } from './master-data-record.service';
 
@Injectable()
export class MasterDataImportService {
  private readonly logger = new Logger(MasterDataImportService.name);
 
  constructor(
    @InjectModel(MdmImportJob.name)
    private readonly jobModel: Model<MdmImportJob>,
    @InjectModel(MdmImportRow.name)
    private readonly rowModel: Model<MdmImportRow>,
    @InjectModel(MdmImportMapping.name)
    private readonly mappingModel: Model<MdmImportMapping>,
    private readonly recordService: MasterDataRecordService,
  ) {}
 
  async createJob(params: {
    definitionKey: string;
    sourceFormat: string;
    sourceFileName?: string;
    importMappingId?: string;
    conflictStrategy: string;
    dryRun?: boolean;
    tenantId?: string;
    userId: string;
  }): Promise<any> {
    const job = await this.jobModel.create({
      definitionKey: params.definitionKey,
      sourceFormat: params.sourceFormat,
      sourceFileName: params.sourceFileName,
      importMappingId: params.importMappingId
        ? new Types.ObjectId(params.importMappingId)
        : undefined,
      conflictStrategy: params.conflictStrategy,
      dryRun: params.dryRun ?? false,
      status: 'queued',
      tenantId: params.tenantId
        ? new Types.ObjectId(params.tenantId)
        : undefined,
      startedBy: new Types.ObjectId(params.userId),
      startedAt: new Date(),
    });
    return job.toObject();
  }
 
  async processRow(jobId: string, rowNumber: number, data: any): Promise<void> {
    const job = await this.jobModel.findById(jobId).exec();
    if (!job) throw new NotFoundException('Import job not found');
 
    const validationErrors: string[] = [];
    if (!data.recordCode) validationErrors.push('Missing recordCode');
    if (!data.recordName) validationErrors.push('Missing recordName');
 
    const status = validationErrors.length > 0 ? 'invalid' : 'valid';
 
    const row = await this.rowModel.create({
      importJobId: new Types.ObjectId(jobId),
      rowNumber,
      rawData: data,
      parsedData: data,
      status,
      validationErrors,
    });
 
    if (status === 'valid' && !job.dryRun) {
      try {
        await this.recordService.create({
          definitionKey: job.definitionKey,
          recordCode: data.recordCode,
          recordName: data.recordName,
          scopeType: job.tenantId ? 'tenant' : 'global',
          scopeId: job.tenantId?.toString(),
          attributes: data.attributes || {},
        });
        row.status = 'imported';
        await row.save();
      } catch (err: any) {
        row.status = 'failed';
        row.errorMessage = err.message;
        await row.save();
      }
    }
  }
}