All files / src/domains/hr/attendance/shift/roster roster.service.ts

0% Statements 0/27
0% Branches 0/18
0% Functions 0/9
0% Lines 0/23

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                                                                                                                                                                                                                                               
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import {
  ShiftRotation,
  ShiftRotationStep,
  ShiftRoster,
  ShiftRosterEntry,
} from '../../schemas';
import {
  CreateShiftRotationDto,
  RotationStepDto,
  CreateShiftRosterDto,
  CreateRosterEntryDto,
  GenerateRosterDto,
} from './dto/roster.dto';
 
@Injectable()
export class RosterService {
  constructor(
    @InjectModel(ShiftRotation.name)
    private rotationModel: Model<ShiftRotation>,
    @InjectModel(ShiftRotationStep.name)
    private rotationStepModel: Model<ShiftRotationStep>,
    @InjectModel(ShiftRoster.name) private rosterModel: Model<ShiftRoster>,
    @InjectModel(ShiftRosterEntry.name)
    private rosterEntryModel: Model<ShiftRosterEntry>,
  ) {}
 
  async createRotation(
    tenantId: string,
    createDto: CreateShiftRotationDto,
    userId: string,
  ) {
    const created = new this.rotationModel({
      ...createDto,
      tenantId,
      createdBy: userId,
    });
    return created.save();
  }
 
  async setRotationSteps(
    tenantId: string,
    rotationId: string,
    steps: RotationStepDto[],
  ) {
    // Basic verification
    const rotation = await this.rotationModel
      .findOne({ _id: rotationId, tenantId })
      .exec();
    if (!rotation) {
      throw new NotFoundException(`Shift rotation #${rotationId} not found`);
    }
 
    await this.rotationStepModel.deleteMany({ tenantId, rotationId });
 
    const stepDocs = steps.map((step) => ({
      ...step,
      tenantId,
      rotationId,
    }));
 
    return this.rotationStepModel.insertMany(stepDocs);
  }
 
  async getRotationSteps(tenantId: string, rotationId: string) {
    return this.rotationStepModel
      .find({ tenantId, rotationId })
      .sort({ sequence: 1 })
      .exec();
  }
 
  async createRoster(
    tenantId: string,
    createDto: CreateShiftRosterDto,
    userId: string,
  ) {
    const created = new this.rosterModel({
      ...createDto,
      tenantId,
      createdBy: userId,
    });
    return created.save();
  }
 
  async addRosterEntries(
    tenantId: string,
    rosterId: string,
    entriesDto: CreateRosterEntryDto[],
  ) {
    // Delete existing entries for the exact dates to avoid duplicates
    // Using simple approach here
    const entries = entriesDto.map((entry) => ({
      ...entry,
      tenantId,
      rosterId,
    }));
    return this.rosterEntryModel.insertMany(entries);
  }
 
  async generateRoster(
    tenantId: string,
    generateDto: GenerateRosterDto,
    userId: string,
  ) {
    // This is a complex logic area
    // 1. Fetch target employees based on targetType and targetId
    // 2. Fetch the rotation steps if rotationId is provided, or use default shifts
    // 3. Iterate through dates from startDate to endDate
    // 4. Assign shifts sequentially based on durationDays of each step
 
    // For now, return a placeholder representing successful job start
    return {
      message: 'Roster generation started',
      details: generateDto,
    };
  }
}