All files / src/domains/procurement/requisitions/services requisition.service.ts

0% Statements 0/43
0% Branches 0/22
0% Functions 0/8
0% Lines 0/36

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 120 121 122 123                                                                                                                                                                                                                                                     
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { PurchaseRequisition } from '../schemas/requisition.schema';
import { EventBusService } from '../../../../platform/events/event-bus.service';
import { AuditLogService } from '../../../../platform/audit/audit-log.service';
 
@Injectable()
export class RequisitionService {
  constructor(
    @InjectModel(PurchaseRequisition.name)
    private readonly requisitionModel: Model<PurchaseRequisition>,
    private readonly eventBus: EventBusService,
    private readonly auditLog: AuditLogService,
  ) {}
 
  async createRequisition(tenantId: string, requesterId: string, data: any, userId: string): Promise<PurchaseRequisition> {
    const count = await this.requisitionModel.countDocuments({ tenantId }).exec();
    const requisitionNumber = `PR-${new Date().getFullYear()}-${(count + 1).toString().padStart(5, '0')}`;
 
    const estimatedCost = data.items.reduce((sum: number, item: any) => sum + (item.quantity * item.estimatedPrice), 0);
 
    // Mock Budget Check logic
    if (data.items.some((item: any) => item.costCenter === 'OVER_BUDGET')) {
      throw new BadRequestException('Request exceeds department budget allocations limit');
    }
 
    const pr = await this.requisitionModel.create({
      ...data,
      tenantId,
      requisitionNumber,
      requesterId,
      estimatedCost,
      status: 'submitted',
    });
 
    await this.eventBus.publish('procurement.requisition.created.v1', {
      requisitionId: (pr as any)._id.toString(),
      tenantId,
      estimatedCost,
    }, tenantId);
 
    await this.auditLog.log({
      tenantId,
      userId,
      action: 'CREATE',
      resource: 'PurchaseRequisition',
      resourceId: (pr as any)._id.toString(),
      newValues: { requisitionNumber, estimatedCost },
    });
 
    return pr;
  }
 
  async getRequisitions(tenantId: string, requesterId?: string): Promise<PurchaseRequisition[]> {
    const query: any = { tenantId };
    if (requesterId) {
      query.requesterId = requesterId;
    }
    return this.requisitionModel.find(query).exec();
  }
 
  async getRequisitionById(tenantId: string, id: string): Promise<PurchaseRequisition> {
    const pr = await this.requisitionModel.findOne({ _id: id, tenantId }).exec();
    if (!pr) throw new NotFoundException('Purchase Requisition not found');
    return pr;
  }
 
  async approveRequisition(tenantId: string, id: string, userId: string): Promise<PurchaseRequisition> {
    const pr = await this.requisitionModel.findOneAndUpdate(
      { _id: id, tenantId, status: 'submitted' },
      { status: 'approved', approvedBy: userId, approvalDate: new Date() },
      { new: true }
    ).exec();
 
    if (!pr) throw new NotFoundException('Purchase Requisition not found or not in submitted status');
 
    await this.eventBus.publish('procurement.requisition.approved.v1', {
      requisitionId: id,
      tenantId,
      approvedBy: userId,
    }, tenantId);
 
    await this.auditLog.log({
      tenantId,
      userId,
      action: 'APPROVE',
      resource: 'PurchaseRequisition',
      resourceId: id,
      newValues: { requisitionNumber: pr.requisitionNumber },
    });
 
    return pr;
  }
 
  async rejectRequisition(tenantId: string, id: string, userId: string): Promise<PurchaseRequisition> {
    const pr = await this.requisitionModel.findOneAndUpdate(
      { _id: id, tenantId, status: 'submitted' },
      { status: 'rejected', approvedBy: userId, approvalDate: new Date() },
      { new: true }
    ).exec();
 
    if (!pr) throw new NotFoundException('Purchase Requisition not found or not in submitted status');
 
    await this.eventBus.publish('procurement.requisition.rejected.v1', {
      requisitionId: id,
      tenantId,
      approvedBy: userId,
    }, tenantId);
 
    await this.auditLog.log({
      tenantId,
      userId,
      action: 'REJECT',
      resource: 'PurchaseRequisition',
      resourceId: id,
      newValues: { requisitionNumber: pr.requisitionNumber },
    });
 
    return pr;
  }
}