UNPKG

@accounter/server

Version:
196 lines (178 loc) • 6.13 kB
import { differenceInDays } from 'date-fns'; import { GraphQLError } from 'graphql'; import { Injector } from 'graphql-modules'; import type { BusinessTripSummary, BusinessTripSummaryCategories, } from '../../../__generated__/types.js'; import { accommodationExpenseDataCollector, AttendeeInfo, BusinessTripError, calculateTotalReportSummaryCategory, carRentalExpensesDataCollector, convertSummaryCategoryDataToRow, flightExpenseDataCollector, onlyUnique, otherExpensesDataCollector, SummaryData, travelAndSubsistenceExpensesDataCollector, } from '../helpers/business-trip-report.helper.js'; import { BusinessTripAttendeesProvider } from '../providers/business-trips-attendees.provider.js'; import { BusinessTripExpensesProvider } from '../providers/business-trips-expenses.provider.js'; import { BusinessTripTaxVariablesProvider } from '../providers/business-trips-tax-variables.provider.js'; import type { BusinessTripProto } from '../types.js'; export async function businessTripSummary( injector: Injector, dbBusinessTrip: BusinessTripProto, ): Promise<BusinessTripSummary> { try { const attendees = await injector .get(BusinessTripAttendeesProvider) .getBusinessTripsAttendeesByBusinessTripIdLoader.load(dbBusinessTrip.id); if (!attendees?.length) { return { rows: [], errors: ['Attendees are not set'], }; } let fromDate: Date | undefined; let toDate: Date | undefined; attendees.map(attendee => { const { arrival, departure } = attendee; if (arrival && (!fromDate || arrival < fromDate)) { fromDate = arrival; } if (departure && (!toDate || departure > toDate)) { toDate = departure; } }); if (!fromDate || !toDate) { return { rows: [], errors: ['Business trip dates are not set'], }; } const expensesPromise = await injector .get(BusinessTripExpensesProvider) .getBusinessTripExtendedExpensesByBusinessTripId(dbBusinessTrip.id); const taxVariablesPromise = injector .get(BusinessTripTaxVariablesProvider) .getTaxVariablesByDateLoader.load(toDate); const [ { flightExpenses, accommodationsExpenses, travelAndSubsistenceExpenses, otherExpenses, carRentalExpenses, }, taxVariables, ] = await Promise.all([expensesPromise, taxVariablesPromise]); if (!taxVariables) { return { rows: [], errors: ['Tax variables are not set'], }; } const summaryData: Partial<SummaryData> = {}; const attendeesMap = new Map<string, AttendeeInfo>(); attendees.map(attendee => { const { arrival, departure } = attendee; let daysCount = 0; let nightsCount = 0; if (arrival && departure) { daysCount = differenceInDays(departure, arrival) + 1; nightsCount = daysCount - 1; } attendeesMap.set(attendee.id, { name: attendee.name, arrival, departure, daysCount, nightsCount, }); }); const errors: string[] = []; const [unAccommodatedDays] = await Promise.all([ accommodationExpenseDataCollector( injector, accommodationsExpenses, summaryData, dbBusinessTrip.destination, taxVariables, attendeesMap, ).catch(e => { if (e instanceof BusinessTripError) { errors.push(e.message); } else { console.error(`Error handling accommodation expenses`, e); throw new GraphQLError((e as Error)?.message ?? `Error handling accommodation expenses`); } }), ...flightExpenses.map(flightExpense => flightExpenseDataCollector(injector, flightExpense, summaryData).catch(e => { if (e instanceof BusinessTripError) { errors.push(e.message); } else { console.error(`Error handling flight expenses`, e); throw new GraphQLError((e as Error)?.message ?? `Error handling flight expenses`); } }), ), otherExpensesDataCollector(injector, otherExpenses, summaryData).catch(e => { if (e instanceof BusinessTripError) { errors.push(e.message); } else { console.error(`Error handling other expenses`, e); throw new GraphQLError((e as Error)?.message ?? `Error handling other expenses`); } }), carRentalExpensesDataCollector( injector, carRentalExpenses, summaryData, taxVariables, dbBusinessTrip.destination, ).catch(e => { if (e instanceof BusinessTripError) { errors.push(e.message); } else { console.error(`Error handling car rental expenses`, e); throw new GraphQLError((e as Error)?.message ?? `Error handling car rental expenses`); } }), ]); await travelAndSubsistenceExpensesDataCollector( injector, travelAndSubsistenceExpenses, summaryData, taxVariables, { destinationCode: dbBusinessTrip.destination, attendees: attendeesMap, unAccommodatedDays: unAccommodatedDays ?? 0, }, ).catch(e => { if (e instanceof BusinessTripError) { errors.push(e.message); } else { console.error(`Error handling travel and subsistence expenses`, e); throw new GraphQLError( (e as Error)?.message ?? `Error handling travel and subsistence expenses`, ); } }); const totalSumCategory = calculateTotalReportSummaryCategory(summaryData); summaryData['TOTAL'] = totalSumCategory; return { rows: Object.entries(summaryData).map(([category, data]) => convertSummaryCategoryDataToRow(category as BusinessTripSummaryCategories, data), ), errors: errors.length ? errors.filter(onlyUnique) : undefined, }; } catch (e) { console.error(`Error fetching business trip expenses`, e); throw new GraphQLError((e as Error)?.message ?? `Error fetching business trip expenses`); } }