File

src/request-order/order-request.service.ts

Index

Properties
Methods

Constructor

constructor(orderRequestModel: Model)
Parameters :
Name Type Optional
orderRequestModel Model<OrderRequestDocument> No

Methods

Async checkIfUserIsAllowedToAccessOrder
checkIfUserIsAllowedToAccessOrder(email: string, orderId: string)
Parameters :
Name Type Optional
email string No
orderId string No
Returns : any
Async create
create(requestOrderDto: RequestOrderDto)
Parameters :
Name Type Optional
requestOrderDto RequestOrderDto No
Returns : unknown
Async findRequestOrderById
findRequestOrderById(id: string)
Parameters :
Name Type Optional
id string No
Returns : unknown
Async findRequestOrderbyOrderId
findRequestOrderbyOrderId(id: string, dealerEmail?: string)
Parameters :
Name Type Optional
id string No
dealerEmail string Yes
Returns : unknown
Async getRequestedOrders
getRequestedOrders(filterRequestOrderDto: FilterRequestOrderDto, colName?: string, valuesIn?: string[])
Parameters :
Name Type Optional
filterRequestOrderDto FilterRequestOrderDto No
colName string Yes
valuesIn string[] Yes
Returns : Promise<LeanDocument[]>
Async update
update(id: string, updateRequestOrderDto)
Parameters :
Name Type Optional
id string No
updateRequestOrderDto No
Returns : Promise<OrderRequestDocument>

Properties

Private Readonly cacheService
Type : Cache
Decorators :
@Inject(CACHE_MANAGER)
Private Readonly emailService
Type : EmailService
Decorators :
@Inject(EmailService)
Private Readonly userService
Type : UserService
Decorators :
@Inject(UserService)
import {
  BadRequestException,
  CACHE_MANAGER,
  Inject,
  Injectable,
  ServiceUnavailableException,
  UnauthorizedException,
} from "@nestjs/common";
import { OrderRequest } from "./entities/orderRequest.entity";
import { InjectModel } from "@nestjs/mongoose";
import { LeanDocument, Model } from "mongoose";
import { OrderRequestDocument } from "./schemas/orderRequest.schema";
import { RequestOrderDto } from "./dtos/create-orderRequest.dto";
import { FilterRequestOrderDto } from "./dtos/filter-orderRequest.dto";
import { ApolloQueryResult } from "@apollo/client";
import { decodeBytes32String, formatEther, formatUnits } from "ethers";
import {
  EndUserRoles,
  IGetDealerIdAndStatusByOrderIdQuery,
  IGetOrderDetailsQuery,
  IOrder,
  RequestStatus,
} from "src/common/interfaces";
import {
  apolloClient,
  getDealerIdAndStatusByOrderIdQuery,
  getOrderDetailsQuery,
} from "src/common/provider";
import { UserService } from "src/user/user.service";
import { EmailService } from "src/common/provider/mail/email.service";
import {
  OrderApprovedTemplate,
  OrderRejectedTemplate,
  OrderArchivedTemplate,
} from "src/common/provider/mail/templates";
import { BLOCKCHAIN_EXPLORER } from "src/common/constants";
import { getFundName } from "src/common/utils";
import { Cache } from "cache-manager";

@Injectable()
export class OrderRequestService {
  @Inject(UserService)
  private readonly userService: UserService;

  @Inject(EmailService)
  private readonly emailService: EmailService;

  @Inject(CACHE_MANAGER)
  private readonly cacheService: Cache;

  constructor(
    @InjectModel(OrderRequest.name)
    private readonly orderRequestModel: Model<OrderRequestDocument>,
  ) {}

  async create(requestOrderDto: RequestOrderDto) {
    return this.orderRequestModel.create(requestOrderDto);
  }

  async update(
    id: string,
    updateRequestOrderDto: FilterRequestOrderDto & { investorId?: string },
  ): Promise<OrderRequestDocument> {
    const [orderRequest, subgraphOrderbook, cachedOrder]: [
      OrderRequestDocument,
      ApolloQueryResult<IGetOrderDetailsQuery>,
      Partial<IOrder>,
    ] = await Promise.all([
      this.orderRequestModel
        .findByIdAndUpdate(id, updateRequestOrderDto, {
          new: true,
        })
        .exec(),
      apolloClient.query({
        query: getOrderDetailsQuery,
        variables: {
          orderBook: updateRequestOrderDto.orderId.split("-")[0],
          orderId: updateRequestOrderDto.orderId,
        },
      }),
      this.cacheService.get(updateRequestOrderDto.orderId),
    ]);
    if (subgraphOrderbook.errors) {
      console.error(subgraphOrderbook.errors[0]);
      throw new ServiceUnavailableException(subgraphOrderbook.errors);
    }

    const order =
      subgraphOrderbook.data.order &&
      subgraphOrderbook.data.order.confirmedTxHash
        ? subgraphOrderbook.data.order
        : cachedOrder;

    const investor = await this.userService.findOne(
      decodeBytes32String(order.onBehalf.id),
    );
    const fundId = decodeBytes32String(
      subgraphOrderbook.data.orderBook.fund.id,
    );
    if (
      !investor.subscribedFunds.find((fund) => fund._id.toString() === fundId)
    ) {
      throw new BadRequestException(
        "Investor is not subscribed to this fund anymore",
      );
    }
    if (!subgraphOrderbook.data.order) {
      return orderRequest;
    }
    let emailTemplate = OrderApprovedTemplate({
      orderType: order.type,
      txHashUrl: `${BLOCKCHAIN_EXPLORER}/tx/${order.confirmedTxHash}`,
      fundName: getFundName(investor, subgraphOrderbook.data.orderBook.fund.id),
      instrumentName:
        subgraphOrderbook.data.orderBook.instrument.securityToken.name,
      txTime: new Date().toUTCString(),
      amount:
        order.type === "Redemption"
          ? `${formatEther(order.amount)} ${
              subgraphOrderbook.data.orderBook.instrument.securityToken.symbol
            }`
          : `${formatUnits(order.amount, 6)} USD`,
      investorName: investor.name,
    });
    if (orderRequest.status === RequestStatus.rejected) {
      emailTemplate = OrderRejectedTemplate({
        orderType: order.type,
        txHashUrl: `${BLOCKCHAIN_EXPLORER}/tx/${order.confirmedTxHash}`,
        fundName: getFundName(
          investor,
          subgraphOrderbook.data.orderBook.fund.id,
        ),
        instrumentName:
          subgraphOrderbook.data.orderBook.instrument.securityToken.name,
        txTime: new Date().toUTCString(),
        amount:
          order.type === "Redemption"
            ? `${formatEther(order.amount)} ${
                subgraphOrderbook.data.orderBook.instrument.securityToken.symbol
              }`
            : `${formatUnits(order.amount, 6)} USD`,
        optionalComment: orderRequest.optionalComment || "No comment provided",
      });
    }
    if (orderRequest.status === RequestStatus.archived) {
      emailTemplate = OrderArchivedTemplate({
        orderType: order.type,
        txHashUrl: `${BLOCKCHAIN_EXPLORER}/tx/${order.confirmedTxHash}`,
        fundName: getFundName(
          investor,
          subgraphOrderbook.data.orderBook.fund.id,
        ),
        instrumentName:
          subgraphOrderbook.data.orderBook.instrument.securityToken.name,
        txTime: new Date().toUTCString(),
        amount:
          order.type === "Redemption"
            ? `${formatEther(order.amount)} ${
                subgraphOrderbook.data.orderBook.instrument.securityToken.symbol
              }`
            : `${formatUnits(order.amount, 6)} USD`,
      });
    }
    await this.emailService.sendEmail({
      from: "yehia@nethermind.io",
      to: [investor.email],
      subject: `Order has been ${orderRequest.status}`,
      html: emailTemplate,
    });

    return orderRequest;
  }
  async findRequestOrderById(id: string) {
    return this.orderRequestModel.findById(id).lean().exec();
  }
  async findRequestOrderbyOrderId(id: string, dealerEmail?: string) {
    if (dealerEmail) {
      await this.checkIfUserIsAllowedToAccessOrder(dealerEmail, id);
    }
    return this.orderRequestModel.findOne({ orderId: id }).lean().exec();
  }
  async checkIfUserIsAllowedToAccessOrder(email: string, orderId: string) {
    const user = await this.userService.findUserByProperty({
      email: email,
    });
    const dealerIdSubgraph: ApolloQueryResult<IGetDealerIdAndStatusByOrderIdQuery> =
      await apolloClient.query({
        query: getDealerIdAndStatusByOrderIdQuery,
        variables: {
          orderId,
        },
      });
    if (!dealerIdSubgraph.data.order) {
      throw new BadRequestException(
        `The order with id ${orderId} does not exist`,
      );
    }
    if (
      user.endUserRole === EndUserRoles.dealer &&
      decodeBytes32String(dealerIdSubgraph.data.order.dealer.id) !==
        user._id.toString()
    ) {
      throw new UnauthorizedException(
        "The dealer is not assigned to this order",
      );
    }

    if (
      user.endUserRole === EndUserRoles.investor &&
      decodeBytes32String(dealerIdSubgraph.data.order.onBehalf.id) !==
        user._id.toString()
    ) {
      throw new UnauthorizedException(
        "The investor is not related to this order",
      );
    }
  }
  async getRequestedOrders(
    filterRequestOrderDto: FilterRequestOrderDto,
    colName?: string,
    valuesIn?: string[],
  ): Promise<LeanDocument<OrderRequestDocument>[]> {
    const { limit, skip, orderDirection, ...restOfFilter } =
      filterRequestOrderDto ?? {};
    return await this.orderRequestModel
      .find({
        [colName]: { $in: valuesIn },
        ...restOfFilter,
      })
      .limit(limit)
      .skip(skip * limit)
      .sort({ created: orderDirection })
      .lean()
      .exec();
  }
}

results matching ""

    No results matching ""