All files / libs/payout-calculation/compute-engine/core-lifecycle/src/lifecycle calculations.service.ts

0% Statements 0/105
0% Branches 0/1
0% Functions 0/1
0% Lines 0/105

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                                                                                                                                                                                                                   
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { keyBy } from 'lodash-es';
import { In, Repository } from 'typeorm';

import { Calculation, Period, Plan, type Company } from '@amalia/core/models';
import { CALCULATION_ONGOING_STATUSES, CalculationStatus, CalculationType } from '@amalia/core/types';
import { canViewThisStatement, defineAbilityFor } from '@amalia/kernel/auth/shared';
import { type AuthenticatedContext } from '@amalia/kernel/auth/types';

@Injectable()
export class CalculationsService {
  public constructor(
    @InjectRepository(Calculation)
    private readonly calculationRepository: Repository<Calculation>,
    @InjectRepository(Period)
    private readonly periodRepository: Repository<Period>,
    @InjectRepository(Plan)
    private readonly planRepository: Repository<Plan>,
  ) {}

  public async getCalculation(company: Company, calculationId: string, relations?: string[]) {
    return this.calculationRepository.findOne({
      where: { company: { id: company.id }, id: calculationId },
      relations,
    });
  }

  public async getOngoingCalculations(company: Company) {
    return this.calculationRepository.find({
      where: { company: { id: company.id }, status: In(CALCULATION_ONGOING_STATUSES) },
      relations: ['creator'],
    });
  }

  public async findCalculations(
    company: Company,
    periodId: string,
    status?: CalculationStatus[],
    count?: number,
    calculationType?: CalculationType,
  ) {
    const query = this.calculationRepository
      .createQueryBuilder('calculation')
      .where('calculation.companyId = :companyId', { companyId: company.id })
      .andWhere('calculation.descriptor @> :periodIdCondition', { periodIdCondition: JSON.stringify([{ periodId }]) });

    if (status && status.length > 0) {
      query.andWhere('calculation.status IN (:...status)', { status });
    }
    if (calculationType) {
      query.andWhere('calculation.type = :calculationType', {
        calculationType,
      });
    }

    query.addOrderBy('"createdAt"', 'DESC');
    query.limit(count || 100);

    return query.getMany();
  }

  public async filterCalculationsByAccessRight(
    authenticatedContext: AuthenticatedContext,
    calculations: Calculation[],
  ): Promise<Calculation[]> {
    const periodIds = calculations.flatMap((calculation) => calculation.descriptor.map((step) => step.periodId));
    const planIds = calculations.flatMap((calculation) =>
      calculation.descriptor.flatMap((step) => step.batches.map((batch) => batch.planId)),
    );

    const periods = await this.periodRepository.find({
      where: { company: { id: authenticatedContext.user.companyId }, id: In(periodIds) },
    });
    const periodsMap = keyBy(periods, 'id');

    const plans = await this.planRepository.find({
      where: { company: { id: authenticatedContext.user.companyId }, id: In(planIds) },
    });
    const plansMap = keyBy(plans, 'id');

    const ability = defineAbilityFor(authenticatedContext);

    return calculations.map((calculation) => ({
      ...calculation,
      descriptor: calculation.descriptor
        .map((step) => ({
          ...step,
          batches: step.batches
            .map((batch) => ({
              ...batch,
              users: batch.users.filter((user) =>
                canViewThisStatement(ability, {
                  user,
                  period: periodsMap[step.periodId],
                  plan: plansMap[batch.planId],
                }),
              ),
            }))
            .filter((batch) => batch.users.length > 0),
        }))
        .filter((step) => step.batches.length > 0),
    }));
  }
}