import moment from 'moment'; import { difference } from 'lodash'; import { Service, Inject } from 'typedi'; import { ServiceError } from 'exceptions'; import { EventDispatcher, EventDispatcherInterface, } from 'decorators/eventDispatcher'; import { IExchangeRateDTO, IExchangeRate, IExchangeRatesService, IExchangeRateEditDTO, IExchangeRateFilter, } from 'interfaces'; import TenancyService from 'services/Tenancy/TenancyService'; const ERRORS = { NOT_FOUND_EXCHANGE_RATES: 'NOT_FOUND_EXCHANGE_RATES', EXCHANGE_RATE_PERIOD_EXISTS: 'EXCHANGE_RATE_PERIOD_EXISTS', EXCHANGE_RATE_NOT_FOUND: 'EXCHANGE_RATE_NOT_FOUND', }; @Service() export default class ExchangeRatesService implements IExchangeRatesService { @Inject('logger') logger: any; @EventDispatcher() eventDispatcher: EventDispatcherInterface; @Inject() tenancy: TenancyService; /** * Creates a new exchange rate. * @param {number} tenantId * @param {IExchangeRateDTO} exchangeRateDTO * @returns {Promise} */ public async newExchangeRate( tenantId: number, exchangeRateDTO: IExchangeRateDTO ): Promise { const { ExchangeRate } = this.tenancy.models(tenantId); this.logger.info('[exchange_rates] trying to insert new exchange rate.', { tenantId, exchangeRateDTO, }); await this.validateExchangeRatePeriodExistance(tenantId, exchangeRateDTO); const exchangeRate = await ExchangeRate.query().insertAndFetch({ ...exchangeRateDTO, date: moment(exchangeRateDTO.date).format('YYYY-MM-DD'), }); this.logger.info('[exchange_rates] inserted successfully.', { tenantId, exchangeRateDTO, }); return exchangeRate; } /** * Edits the exchange rate details. * @param {number} tenantId - Tenant id. * @param {number} exchangeRateId - Exchange rate id. * @param {IExchangeRateEditDTO} editExRateDTO - Edit exchange rate DTO. */ public async editExchangeRate( tenantId: number, exchangeRateId: number, editExRateDTO: IExchangeRateEditDTO ): Promise { const { ExchangeRate } = this.tenancy.models(tenantId); this.logger.info('[exchange_rates] trying to edit exchange rate.', { tenantId, exchangeRateId, editExRateDTO, }); await this.validateExchangeRateExistance(tenantId, exchangeRateId); await ExchangeRate.query() .where('id', exchangeRateId) .update({ ...editExRateDTO }); this.logger.info('[exchange_rates] exchange rate edited successfully.', { tenantId, exchangeRateId, editExRateDTO, }); } /** * Deletes the given exchange rate. * @param {number} tenantId - Tenant id. * @param {number} exchangeRateId - Exchange rate id. */ public async deleteExchangeRate( tenantId: number, exchangeRateId: number ): Promise { const { ExchangeRate } = this.tenancy.models(tenantId); await this.validateExchangeRateExistance(tenantId, exchangeRateId); await ExchangeRate.query().findById(exchangeRateId).delete(); } /** * Listing exchange rates details. * @param {number} tenantId - Tenant id. * @param {IExchangeRateFilter} exchangeRateFilter - Exchange rates list filter. */ public async listExchangeRates( tenantId: number, exchangeRateFilter: IExchangeRateFilter ): Promise { const { ExchangeRate } = this.tenancy.models(tenantId); const exchangeRates = await ExchangeRate.query().pagination( exchangeRateFilter.page - 1, exchangeRateFilter.pageSize ); return exchangeRates; } /** * Deletes exchange rates in bulk. * @param {number} tenantId * @param {number[]} exchangeRatesIds */ public async deleteBulkExchangeRates( tenantId: number, exchangeRatesIds: number[] ): Promise { const { ExchangeRate } = this.tenancy.models(tenantId); this.logger.info('[exchange_rates] trying delete in bulk.', { tenantId, exchangeRatesIds, }); await this.validateExchangeRatesIdsExistance(tenantId, exchangeRatesIds); await ExchangeRate.query().whereIn('id', exchangeRatesIds).delete(); this.logger.info('[exchange_rates] deleted successfully.', { tenantId, exchangeRatesIds, }); } /** * Validates period of the exchange rate existance. * @param {number} tenantId - Tenant id. * @param {IExchangeRateDTO} exchangeRateDTO - Exchange rate DTO. * @return {Promise} */ private async validateExchangeRatePeriodExistance( tenantId: number, exchangeRateDTO: IExchangeRateDTO ): Promise { const { ExchangeRate } = this.tenancy.models(tenantId); this.logger.info('[exchange_rates] trying to validate period existance.', { tenantId, }); const foundExchangeRate = await ExchangeRate.query() .where('currency_code', exchangeRateDTO.currencyCode) .where('date', exchangeRateDTO.date); if (foundExchangeRate.length > 0) { this.logger.info('[exchange_rates] given exchange rate period exists.', { tenantId, }); throw new ServiceError(ERRORS.EXCHANGE_RATE_PERIOD_EXISTS); } } /** * Validate the given echange rate id existance. * @param {number} tenantId - Tenant id. * @param {number} exchangeRateId - Exchange rate id. * @returns {Promise} */ private async validateExchangeRateExistance( tenantId: number, exchangeRateId: number ) { const { ExchangeRate } = this.tenancy.models(tenantId); this.logger.info( '[exchange_rates] trying to validate exchange rate id existance.', { tenantId, exchangeRateId } ); const foundExchangeRate = await ExchangeRate.query().findById( exchangeRateId ); if (!foundExchangeRate) { this.logger.info('[exchange_rates] exchange rate not found.', { tenantId, exchangeRateId, }); throw new ServiceError(ERRORS.EXCHANGE_RATE_NOT_FOUND); } } /** * Validates exchange rates ids existance. * @param {number} tenantId - Tenant id. * @param {number[]} exchangeRatesIds - Exchange rates ids. * @returns {Promise} */ private async validateExchangeRatesIdsExistance( tenantId: number, exchangeRatesIds: number[] ): Promise { const { ExchangeRate } = this.tenancy.models(tenantId); const storedExchangeRates = await ExchangeRate.query().whereIn( 'id', exchangeRatesIds ); const storedExchangeRatesIds = storedExchangeRates.map( (category) => category.id ); const notFoundExRates = difference( exchangeRatesIds, storedExchangeRatesIds ); if (notFoundExRates.length > 0) { throw new ServiceError(ERRORS.NOT_FOUND_EXCHANGE_RATES); } } }