UNPKG

@accounter/server

Version:
235 lines (209 loc) • 7.81 kB
import DataLoader from 'dataloader'; import { Injectable, Scope } from 'graphql-modules'; import { sql } from '@pgtyped/runtime'; import { reassureOwnerIdExists } from '../../../shared/helpers/index.js'; import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js'; import { TenantAwareDBClient } from '../../app-providers/tenant-db-client.js'; import type { IDeleteExpenseParams, IDeleteExpenseQuery, IGetExpensesByChargeIdsQuery, IGetExpensesByChargeIdsResult, IGetExpensesByFinancialEntityIdsQuery, IGetExpensesByIdsQuery, IInsertExpenseParams, IInsertExpenseQuery, IInsertExpensesParams, IInsertExpensesQuery, IReplaceMiscExpensesChargeIdParams, IReplaceMiscExpensesChargeIdQuery, IUpdateExpenseParams, IUpdateExpenseQuery, } from '../types.js'; const getExpensesByChargeIds = sql<IGetExpensesByChargeIdsQuery>` SELECT * FROM accounter_schema.misc_expenses WHERE charge_id IN $$chargeIds;`; const getExpensesByIds = sql<IGetExpensesByIdsQuery>` SELECT * FROM accounter_schema.misc_expenses WHERE id IN $$ids;`; const getExpensesByFinancialEntityIds = sql<IGetExpensesByFinancialEntityIdsQuery>` SELECT * FROM accounter_schema.misc_expenses WHERE creditor_id IN $$financialEntityIds OR debtor_id IN $$financialEntityIds;`; const updateExpense = sql<IUpdateExpenseQuery>` UPDATE accounter_schema.misc_expenses SET charge_id = COALESCE( $chargeId, charge_id ), creditor_id = COALESCE( $creditorId, creditor_id ), debtor_id = COALESCE( $debtorId, debtor_id ), amount = COALESCE( $amount, amount ), currency = COALESCE( $currency, currency ), description = COALESCE( $description, description ), invoice_date = COALESCE( $invoiceDate, invoice_date ), value_date = COALESCE( $valueDate, value_date ) WHERE id = $miscExpenseId RETURNING *;`; const replaceMiscExpensesChargeId = sql<IReplaceMiscExpensesChargeIdQuery>` UPDATE accounter_schema.misc_expenses SET charge_id = $assertChargeID WHERE charge_id = $replaceChargeID RETURNING * `; const insertExpense = sql<IInsertExpenseQuery>` INSERT INTO accounter_schema.misc_expenses (charge_id, creditor_id, debtor_id, amount, currency, description, invoice_date, value_date, owner_id) VALUES ($chargeId, $creditorId, $debtorId, $amount, $currency, $description, $invoiceDate, $valueDate, $ownerId) RETURNING *;`; const insertExpenses = sql<IInsertExpensesQuery>` INSERT INTO accounter_schema.misc_expenses (charge_id, creditor_id, debtor_id, amount, currency, description, invoice_date, value_date, owner_id) VALUES $$miscExpenses(chargeId, creditorId, debtorId, amount, currency, description, invoiceDate, valueDate, ownerId) RETURNING *;`; const deleteExpense = sql<IDeleteExpenseQuery>` DELETE FROM accounter_schema.misc_expenses WHERE id = $id RETURNING id;`; @Injectable({ scope: Scope.Operation, global: true, }) export class MiscExpensesProvider { constructor( private db: TenantAwareDBClient, private adminContextProvider: AdminContextProvider, ) {} private async batchExpensesByChargeIds(chargeIds: readonly string[]) { const expenses = await getExpensesByChargeIds.run({ chargeIds }, this.db); const expensesByChargeId = new Map<string, IGetExpensesByChargeIdsResult[]>(); expenses.map(expense => { if (expensesByChargeId.has(expense.charge_id)) { expensesByChargeId.get(expense.charge_id)?.push(expense); } else { expensesByChargeId.set(expense.charge_id, [expense]); } }); return chargeIds.map(id => expensesByChargeId.get(id) ?? []); } public getExpensesByChargeIdLoader = new DataLoader((chargeIds: readonly string[]) => this.batchExpensesByChargeIds(chargeIds), ); private async batchExpensesByFinancialEntityIds(financialEntityIds: readonly string[]) { const expenses = await getExpensesByFinancialEntityIds.run({ financialEntityIds }, this.db); const expensesByFinancialEntityId = new Map<string, IGetExpensesByChargeIdsResult[]>(); expenses.map(expense => { if (expensesByFinancialEntityId.has(expense.creditor_id)) { expensesByFinancialEntityId.get(expense.creditor_id)?.push(expense); } else { expensesByFinancialEntityId.set(expense.creditor_id, [expense]); } if (expensesByFinancialEntityId.has(expense.debtor_id)) { expensesByFinancialEntityId.get(expense.debtor_id)?.push(expense); } else { expensesByFinancialEntityId.set(expense.debtor_id, [expense]); } }); return financialEntityIds.map(id => expensesByFinancialEntityId.get(id) ?? []); } public getExpensesByFinancialEntityIdLoader = new DataLoader( (financialEntityIds: readonly string[]) => this.batchExpensesByFinancialEntityIds(financialEntityIds), ); private async batchExpensesByIds(ids: readonly string[]) { const expenses = await getExpensesByIds.run({ ids }, this.db); return ids.map(id => expenses.find(expense => expense.id === id)); } public getExpensesByIdLoader = new DataLoader((ids: readonly string[]) => this.batchExpensesByIds(ids), ); public async updateExpense(params: IUpdateExpenseParams) { if (params.miscExpenseId) await this.invalidateById(params.miscExpenseId); return updateExpense.run(params, this.db); } public async replaceMiscExpensesChargeId(params: IReplaceMiscExpensesChargeIdParams) { if (params.assertChargeID) { await this.invalidateByChargeId(params.assertChargeID); } if (params.replaceChargeID) { await this.invalidateByChargeId(params.replaceChargeID); } return replaceMiscExpensesChargeId.run(params, this.db); } public async insertExpense(params: IInsertExpenseParams) { if (params.chargeId) await this.invalidateByChargeId(params.chargeId); const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext(); return insertExpense.run(reassureOwnerIdExists(params, ownerId), this.db); } public async insertExpenses(params: IInsertExpensesParams) { const chargeIds = Array.from( new Set(params.miscExpenses.map(({ chargeId }) => chargeId)), ).filter(id => !!id) as string[]; if (chargeIds.length) { await Promise.all(chargeIds.map(chargeId => this.invalidateByChargeId(chargeId))); } const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext(); return insertExpenses.run( { miscExpenses: params.miscExpenses.map(expense => ({ ...expense, ownerId: expense.ownerId || ownerId, })), }, this.db, ); } public async deleteMiscExpense(params: IDeleteExpenseParams) { if (params.id) await this.invalidateById(params.id); return deleteExpense.run(params, this.db); } public async invalidateByChargeId(chargeId: string) { this.getExpensesByChargeIdLoader.clear(chargeId); } public async invalidateByFinancialEntityId(financialEntityId: string) { this.getExpensesByFinancialEntityIdLoader.clear(financialEntityId); } public async invalidateById(id: string) { const expense = await this.getExpensesByIdLoader.load(id); if (expense) { this.getExpensesByChargeIdLoader.clear(expense.charge_id); if (expense.creditor_id) { this.getExpensesByFinancialEntityIdLoader.clear(expense.creditor_id); } if (expense.debtor_id) { this.getExpensesByFinancialEntityIdLoader.clear(expense.debtor_id); } } this.getExpensesByIdLoader.clear(id); } public clearCache() { this.getExpensesByIdLoader.clearAll(); this.getExpensesByChargeIdLoader.clearAll(); this.getExpensesByFinancialEntityIdLoader.clearAll(); } }