All files / src/domains/hr/documents document.service.ts

0% Statements 0/39
0% Branches 0/21
0% Functions 0/10
0% Lines 0/33

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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119                                                                                                                                                                                                                                             
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import {
  EmployeeDocumentType,
  EmployeeDocumentLink,
} from './schemas/document.schema';
import { AuditLogService } from '../../../platform/audit/audit-log.service';
import { EventBusService } from '../../../platform/events/event-bus.service';
 
@Injectable()
export class EmployeeDocumentService {
  constructor(
    @InjectModel(EmployeeDocumentType.name)
    private readonly docTypeModel: Model<EmployeeDocumentType>,
    @InjectModel(EmployeeDocumentLink.name)
    private readonly docLinkModel: Model<EmployeeDocumentLink>,
    private readonly auditLog: AuditLogService,
    private readonly eventBus: EventBusService,
  ) {}
 
  // ── Document Types ────────────────────────────────────────────────────────
  async createDocumentType(tenantId: string, data: any) {
    return this.docTypeModel.create({ tenantId, ...data });
  }
 
  async getDocumentTypes(tenantId: string) {
    return this.docTypeModel.find({ tenantId, status: 'active' }).lean().exec();
  }
 
  // ── Document Links ────────────────────────────────────────────────────────
  async uploadDocument(
    tenantId: string,
    employeeId: string,
    data: any,
    userId: string,
  ) {
    const link = await this.docLinkModel.create({
      tenantId,
      employeeId,
      ...data,
      uploadedBy: userId,
    });
    await this.auditLog.log({
      tenantId,
      userId,
      action: 'UPLOAD_DOCUMENT',
      resource: 'Employee',
      resourceId: employeeId,
      newValues: { documentTypeId: data.documentTypeId, fileId: data.fileId },
      moduleName: 'HR',
    });
    await this.eventBus.publish(
      'hr.employee.document-uploaded.v1',
      { tenantId, employeeId, documentId: link.id },
      tenantId,
    );
    return link;
  }
 
  async getEmployeeDocuments(tenantId: string, employeeId: string) {
    return this.docLinkModel
      .find({ tenantId, employeeId })
      .sort({ createdAt: -1 })
      .lean()
      .exec();
  }
 
  async verifyDocument(
    tenantId: string,
    documentId: string,
    status: 'verified' | 'rejected',
    userId: string,
    rejectionReason?: string,
  ) {
    const doc = await this.docLinkModel
      .findOne({ tenantId, _id: documentId })
      .exec();
    if (!doc) throw new NotFoundException('Document not found');
    doc.verificationStatus = status;
    doc.verifiedBy = userId;
    doc.verifiedAt = new Date();
    if (rejectionReason) doc.rejectionReason = rejectionReason;
    await doc.save();
    return doc;
  }
 
  async getExpiringDocuments(tenantId: string, days = 30) {
    const until = new Date();
    until.setDate(until.getDate() + days);
    return this.docLinkModel
      .find({
        tenantId,
        expiryDate: { $lte: until, $gte: new Date() },
        verificationStatus: { $ne: 'rejected' },
      })
      .sort({ expiryDate: 1 })
      .lean()
      .exec();
  }
 
  async getDocumentChecklist(tenantId: string, employeeId: string) {
    const types = await this.docTypeModel
      .find({ tenantId, mandatory: true, status: 'active' })
      .lean()
      .exec();
    const links = await this.docLinkModel
      .find({ tenantId, employeeId })
      .lean()
      .exec();
    const uploadedTypeIds = new Set(links.map((l: any) => l.documentTypeId));
 
    return types.map((t: any) => ({
      ...t,
      uploaded: uploadedTypeIds.has(t._id.toString()),
    }));
  }
}