Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
7.6 kB
2
Indexable
import { add, compare, isZero } from "dinero.js";
import * as A from "fp-ts/Array";
import { flow } from "fp-ts/lib/function";
import * as ROA from "fp-ts/ReadonlyArray";
import { DateTime } from "luxon";

import {
  AumResult,
  RelatedUsersOfRep,
} from "@lambdas/shared/businessLogic/collectFirmReportInfo/types";
import { FaFirm } from "@lambdas/shared/models/FaFirm";
import { ActiveFaRep } from "@lambdas/shared/models/FaRep";
import {
  RelationshipType,
  UserFaRelationshipWithUser,
} from "@lambdas/shared/models/UserFaRelationship";
import { fromCode, SupportedCurrencies } from "@lambdas/shared/money";

import * as ReportData from "./ReportData";
import { FaReportData, FirmReportData } from "./types";

export const collectFirmReportData = (
  faFirm: FaFirm,
  date: DateTime,
  currency: SupportedCurrencies,
  repInfos: ReadonlyArray<
    Readonly<{
      relatedUserInfo: RelatedUsersOfRep;
      aumInfo: AumResult;
    }>
  >,
  unassignedLeadsInfo: Readonly<{
    allTimeUnassignedLeads: UserFaRelationshipWithUser[];
    monthlyUnassignedLeads: UserFaRelationshipWithUser[];
    aumInfo: AumResult;
  }>
): FirmReportData => {
  const repReportData = filterEmptyFaRepData(
    repInfos.map(composeRepReportData(currency))
  );

  const totalMonthlyData = calculateTotalReportData(currency)(
    repReportData.map((report) => report.monthlyData)
  );
  const totalAllTimeData = calculateTotalReportData(currency)(
    repReportData.map((report) => report.allTimeData)
  );
  // const unassignedLeadReportData = composeUnassignedleadReportData(unassignedLeadsInfo.allTimeUnassignedLeads, unassignedLeadsInfo.monthlyUnassignedLeads, unassignedLeadsInfo.aumInfo, currency)
const monthlyUnassignedLead = calculateReportFirmData(
    unassignedLeadsInfo.allTimeUnassignedLeads,
    unassignedLeadsInfo.aumInfo,
    currency
  );
  const unassignedLeadReportData = nonEmptyFirmData(composeUnassignedleadReportData(unassignedLeadsInfo.allTimeUnassignedLeads, unassignedLeadsInfo.monthlyUnassignedLeads, unassignedLeadsInfo.aumInfo, currency))

  return {
    date,
    currency,
    faFirmId: faFirm.id,
    faFirmName: faFirm.name,
    faReps: repReportData.sort((firstFaRep, secondFaRep) =>
      compare(secondFaRep.allTimeData.totalAum, firstFaRep.allTimeData.totalAum)
    ),
    faFirmCreatedDate: faFirm.createdDate.toFormat("MM yy"),
    totalMonthlyData: {
      ...totalMonthlyData,
      numberOfDirect:
        totalMonthlyData.numberOfDirect + unassignedLeadReportData?.monthlyData.numberOfDirect,
      numberOfAssigned:
        totalMonthlyData.numberOfAssigned +
        unassignedLeadReportData?.monthlyData.numberOfAssigned,
      numberOfIndirect: totalMonthlyData.numberOfIndirect,
      aum: add(totalMonthlyData.aum, unassignedLeadReportData?.monthlyData.aum),
    },
    totalAllTimeData: {
      ...totalAllTimeData,
      numberOfDirect:
        totalAllTimeData.numberOfDirect + allTimeUnassignedLead.numberOfDirect,
      numberOfAssigned:
        totalAllTimeData.numberOfAssigned +
        allTimeUnassignedLead.numberOfAssigned,
      numberOfIndirect: totalAllTimeData.numberOfIndirect,
      aum: add(totalAllTimeData.aum, allTimeUnassignedLead.aum),
      totalAum: add(totalAllTimeData.totalAum, allTimeUnassignedLead.aum),
    },
    unassignedLeadsInfo: {
      monthlyData: monthlyUnassignedLead,
      allTimeData: allTimeUnassignedLead,
    },
  };
};

const isNonEmptyFaRepData = ({ monthlyData, allTimeData }: FaReportData) =>
  !isZero(monthlyData.aum) ||
  monthlyData.numberOfDirect !== 0 ||
  monthlyData.numberOfIndirect !== 0 ||
  monthlyData.numberOfAssigned !== 0 ||
  !isZero(allTimeData.personalInvest) ||
  !isZero(allTimeData.aum) ||
  allTimeData.numberOfDirect !== 0 ||
  allTimeData.numberOfIndirect !== 0 ||
  allTimeData.numberOfAssigned !== 0;

const nonEmptyFirmData = ({ unassignedLeadsInfo }: Pick<
  FirmReportData,
  'unassignedLeadsInfo'
>) =>
  (!isZero(unassignedLeadsInfo.monthlyData.aum) ||
    unassignedLeadsInfo.monthlyData.numberOfAssigned !== 0 ||
    unassignedLeadsInfo.monthlyData.numberOfDirect !== 0 ||
    unassignedLeadsInfo.monthlyData.amount !== 0 ||
    !isZero(unassignedLeadsInfo.allTimeData.aum) ||
    unassignedLeadsInfo.allTimeData.amount !== 0 ||
    unassignedLeadsInfo.allTimeData.numberOfAssigned !== 0 ||
    unassignedLeadsInfo.allTimeData.numberOfDirect !== 0) 
  ? unassignedLeadsInfo 
  : undefined;

const filterEmptyFaRepData = A.filter(isNonEmptyFaRepData);

const calculateTotalReportData = (currency: SupportedCurrencies) =>
  flow(ROA.reduce(ReportData.empty(currency), ReportData.plus));

const composeRepReportData =
  (currency: SupportedCurrencies) =>
    ({
      relatedUserInfo,
      aumInfo,
    }: {
      relatedUserInfo: RelatedUsersOfRep;
      aumInfo: AumResult;
    }): FaReportData => {
      const monthlyData = calculateReportData(
        relatedUserInfo.faRep,
        relatedUserInfo.currentMonthUsers,
        aumInfo,
        currency
      );

      const allTimeData = calculateReportData(
        relatedUserInfo.faRep,
        relatedUserInfo.allTimeUsers,
        aumInfo,
        currency
      );

      return {
        name: relatedUserInfo.faRep.name,
        email: relatedUserInfo.faRep.email,
        rnf: relatedUserInfo.faRep.rnf,
        monthlyData,
        allTimeData,
      };
    };

const composeUnassignedleadReportData = (
  allTimeUnassignedLeads: UserFaRelationshipWithUser[],
  monthlyUnassignedLeads: UserFaRelationshipWithUser[],
  aumInfo: AumResult,
  currency: SupportedCurrencies
): Pick<
  FirmReportData,
  'unassignedLeadsInfo'
> => {
  const monthlyData = calculateReportFirmData(
    monthlyUnassignedLeads,
    aumInfo,
    currency
  );

  const allTimeData = calculateReportFirmData(
    allTimeUnassignedLeads,
    aumInfo,
    currency
  );

  return {
    unassignedLeadsInfo: {
      monthlyData,
      allTimeData
    }
  }
}

const calculateReportData = (
  faRep: ActiveFaRep,
  userRelationships: UserFaRelationshipWithUser[],
  aumInfo: AumResult,
  currency: SupportedCurrencies
): ReportData.ReportData => {
  const zero = fromCode(currency).zero;
  const numberOfDirect = userRelationships.filter(
    (user) => user.relationshipType === RelationshipType.DirectRep
  ).length;
  const numberOfIndirect = userRelationships.filter(
    (user) => user.relationshipType === RelationshipType.IndirectRep
  ).length;
  const numberOfAssigned = userRelationships.filter(
    (user) => user.relationshipType === RelationshipType.AssignedRep
  ).length;
  const personalInvest = aumInfo[faRep.userId!] || zero;
  const aum = userRelationships.reduce(
    (total, cur) => add(total, aumInfo[cur.user.id] || zero),
    fromCode(currency).zero
  );
  const totalAum = add(personalInvest, aum);

  return {
    numberOfDirect,
    numberOfAssigned,
    numberOfIndirect,
    aum,
    personalInvest,
    totalAum,
  };
};

const calculateReportFirmData = (
  userRelationships: UserFaRelationshipWithUser[],
  aumInfo: AumResult,
  currency: SupportedCurrencies
): ReportData.ReportFirmData => {
  const zero = fromCode(currency).zero;
  const numberOfDirect = userRelationships.filter(
    (user) => user.relationshipType === RelationshipType.DirectFirm
  ).length;
  const numberOfAssigned = userRelationships.filter(
    (user) => user.relationshipType === RelationshipType.AssignedFirm
  ).length;
  const aum = userRelationships.reduce(
    (total, cur) => add(total, aumInfo[cur.userId] || zero),
    fromCode(currency).zero
  );

  return {
    numberOfDirect,
    numberOfAssigned,
    aum,
    amount: userRelationships.length,
  };
};