penisularhr / src / modules / rainfall / rainfall.service.ts
rainfall.service.ts
Raw
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import moment from 'moment';
import { type FindOptionsWhere, Repository } from 'typeorm';

import { type PageDto } from '../../common/dto/page.dto';
import {
  RainfallTimeframeExists,
  ToTimeEarlierThanFromTimeException,
} from '../../exceptions';
import { type GetRainfallDto } from '../report/dtos/get-report-rainfall.dto';
import { type CreateRainfallRecordDto } from './dtos/create-rainfall.dto';
import { type RainfallRecordPageOptionsDto } from './dtos/get-rainfall-page.dto';
import { type RainfallRecordDto } from './dtos/rainfall.dto';
import { type UpdateRainfallRecordDto } from './dtos/update-rainfall.dto';
import { RainfallRecordEntity } from './rainfall.entity';

@Injectable()
export class RainfallRecordService {
  constructor(
    @InjectRepository(RainfallRecordEntity)
    private rainfallRepository: Repository<RainfallRecordEntity>,
  ) {}

  async findOne(
    findData?: FindOptionsWhere<RainfallRecordEntity>,
  ): Promise<RainfallRecordEntity | null> {
    const queryBuilder = this.rainfallRepository.createQueryBuilder('rainfall');

    if (findData) {
      queryBuilder.where(findData);
    }

    return queryBuilder.getOne();
  }

  async findOneByTimeFrame(
    timeFrame: Date,
  ): Promise<RainfallRecordEntity | null> {
    const queryBuilder = this.rainfallRepository.createQueryBuilder('rainfall');

    queryBuilder
      .where('rainfall.fromTime <= :fromTime', { fromTime: timeFrame })
      .andWhere('rainfall.toTime >= :toTime', { toTime: timeFrame });

    return queryBuilder.getOne();
  }

  async findMany(
    pageOptionsDto: RainfallRecordPageOptionsDto,
  ): Promise<PageDto<RainfallRecordDto>> {
    const { fromTime, toTime, order } = pageOptionsDto;

    const queryBuilder = this.rainfallRepository.createQueryBuilder('rainfall');

    if (fromTime) {
      queryBuilder.andWhere('rainfall.fromTime >= :fromTime', { fromTime });
    }

    if (toTime) {
      queryBuilder.andWhere('rainfall.toTime <= :toTime', { toTime });
    }

    queryBuilder.orderBy('rainfall.fromTime', order);

    const [items, pageMetaDto] = await queryBuilder.paginate(pageOptionsDto);

    return items.toPageDto(pageMetaDto);
  }

  async findReportMany(dto: GetRainfallDto) {
    const { monthFrom, monthTo, order, year } = dto;

    const currentYear = year ?? moment.utc().year();

    const queryBuilder = this.rainfallRepository.createQueryBuilder('rainfall');

    let fromDate: Date | undefined;
    let toDate: Date | undefined;

    if (monthFrom) {
      fromDate = moment
        .utc({
          year: currentYear,
          month: monthFrom - 1,
          date: 1,
        })
        .toDate();

      queryBuilder.andWhere('rainfall.fromTime >= :fromDate', { fromDate });
    }

    if (monthTo) {
      toDate = moment
        .utc({ year: currentYear, month: monthTo - 1, date: 1 })
        .add(1, 'months')
        .toDate();

      queryBuilder.andWhere('rainfall.toTime < :toDate', { toDate });
    }

    queryBuilder.orderBy('rainfall.fromTime', order);

    const response = await queryBuilder.getMany();

    return response.map((el) => el.toDto());
  }

  async createRainfall(
    dto: CreateRainfallRecordDto,
  ): Promise<RainfallRecordEntity> {
    const { fromTime, toTime } = dto;

    if (toTime < fromTime) {
      throw new ToTimeEarlierThanFromTimeException();
    }

    const fromTimeEntity = await this.findOneByTimeFrame(fromTime);
    const toTimeEntity = await this.findOneByTimeFrame(toTime);

    if (fromTimeEntity || toTimeEntity) {
      throw new RainfallTimeframeExists();
    }

    const rainfallEntity = this.rainfallRepository.create(dto);

    await this.rainfallRepository.save(rainfallEntity);

    return rainfallEntity;
  }

  async updateRainfall(
    rainfallRecordEntity: RainfallRecordEntity,
    dto: UpdateRainfallRecordDto,
  ): Promise<RainfallRecordEntity> {
    const { amount, fromTime, toTime } = dto;

    if (amount !== undefined) {
      rainfallRecordEntity.amount = amount;
    }

    if (fromTime) {
      rainfallRecordEntity.fromTime = fromTime;
    }

    if (toTime) {
      if (toTime < rainfallRecordEntity.fromTime) {
        throw new ToTimeEarlierThanFromTimeException();
      }

      rainfallRecordEntity.toTime = toTime;
    }

    await this.rainfallRepository.save(rainfallRecordEntity);

    return rainfallRecordEntity;
  }

  async deleteRainfall(
    rainfallRecordEntity: RainfallRecordEntity,
  ): Promise<void> {
    await this.rainfallRepository.remove(rainfallRecordEntity);
  }
}