UNPKG

@accounter/server

Version:
309 lines (277 loc) • 9.18 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 { IDeleteIssuedDocumentParams, IDeleteIssuedDocumentQuery, IGetAllIssuedDocumentsParams, IGetAllIssuedDocumentsQuery, IGetAllIssuedDocumentsResult, IGetIssuedDocumentsByClientIdsQuery, IGetIssuedDocumentsByExternalIdsQuery, IGetIssuedDocumentsByIdsQuery, IGetIssuedDocumentsByTypeParams, IGetIssuedDocumentsByTypeQuery, IGetIssuedDocumentsStatusByChargeIdsQuery, IInsertIssuedDocumentsParams, IInsertIssuedDocumentsQuery, IUpdateIssuedDocumentByExternalIdParams, IUpdateIssuedDocumentByExternalIdQuery, IUpdateIssuedDocumentParams, IUpdateIssuedDocumentQuery, } from '../types.js'; const getAllIssuedDocuments = sql<IGetAllIssuedDocumentsQuery>` SELECT * FROM accounter_schema.documents_issued; `; const getIssuedDocumentsByIds = sql<IGetIssuedDocumentsByIdsQuery>` SELECT * FROM accounter_schema.documents_issued WHERE id IN $$Ids; `; const getIssuedDocumentsByExternalIds = sql<IGetIssuedDocumentsByExternalIdsQuery>` SELECT * FROM accounter_schema.documents_issued WHERE external_id IN $$externalIds; `; const getIssuedDocumentsByClientIds = sql<IGetIssuedDocumentsByClientIdsQuery>` SELECT d.*, di.external_id, di.status, di.linked_document_ids FROM accounter_schema.documents_issued di LEFT JOIN accounter_schema.documents d ON di.id = d.id WHERE d.debtor_id IN $$clientIds OR d.creditor_id IN $$clientIds; `; const getIssuedDocumentsStatusByChargeIds = sql<IGetIssuedDocumentsStatusByChargeIdsQuery>` SELECT documents.charge_id, COALESCE( bool_or( documents_issued.status = 'OPEN'::accounter_schema.document_status ), false ) AS open_docs_flag FROM accounter_schema.documents_issued LEFT JOIN accounter_schema.documents ON documents.id = documents_issued.id WHERE documents.charge_id in $$chargeIds GROUP BY documents.charge_id `; const getIssuedDocumentsByType = sql<IGetIssuedDocumentsByTypeQuery>` SELECT d.*, di.external_id, di.status, di.linked_document_ids FROM accounter_schema.documents_issued di LEFT JOIN accounter_schema.documents d ON di.id = d.id WHERE d.type = $type ORDER BY d.serial_number DESC LIMIT $limit; `; const updateIssuedDocument = sql<IUpdateIssuedDocumentQuery>` UPDATE accounter_schema.documents_issued SET external_id = COALESCE( $externalId, external_id ), status = COALESCE( $status, status ), linked_document_ids = COALESCE( $linkedDocumentIds, linked_document_ids ) WHERE id = $documentId RETURNING *; `; const updateIssuedDocumentByExternalId = sql<IUpdateIssuedDocumentByExternalIdQuery>` UPDATE accounter_schema.documents_issued SET id = COALESCE( $documentId, id ), status = COALESCE( $status, status ), linked_document_ids = COALESCE( $linkedDocumentIds, linked_document_ids ) WHERE external_id = $externalId RETURNING *; `; const deleteIssuedDocument = sql<IDeleteIssuedDocumentQuery>` DELETE FROM accounter_schema.documents_issued WHERE id = $documentId RETURNING id; `; const insertIssuedDocuments = sql<IInsertIssuedDocumentsQuery>` INSERT INTO accounter_schema.documents_issued ( id, external_id, status, linked_document_ids, owner_id ) VALUES $$issuedDocuments( id, externalId, status, linkedDocumentIds, ownerId ) RETURNING *;`; @Injectable({ scope: Scope.Operation, global: true, }) export class IssuedDocumentsProvider { constructor( private db: TenantAwareDBClient, private adminContextProvider: AdminContextProvider, ) {} private allIssuedDocumentsCache: Promise<IGetAllIssuedDocumentsResult[]> | null = null; public async getAllIssuedDocuments(params: IGetAllIssuedDocumentsParams) { if (this.allIssuedDocumentsCache) { return this.allIssuedDocumentsCache; } this.allIssuedDocumentsCache = getAllIssuedDocuments.run(params, this.db).then(docs => { docs.map(doc => { this.getIssuedDocumentsByIdLoader.prime(doc.id, doc); }); return docs; }); return this.allIssuedDocumentsCache; } private async batchIssuedDocumentsByIds(ids: readonly string[]) { const uniqueIDs = [...new Set(ids)]; try { const docs = await getIssuedDocumentsByIds.run({ Ids: uniqueIDs }, this.db); return ids.map(id => docs.find(doc => doc.id === id)); } catch (e) { console.error(e); return ids.map(() => null); } } public getIssuedDocumentsByIdLoader = new DataLoader((ids: readonly string[]) => this.batchIssuedDocumentsByIds(ids), ); private async batchIssuedDocumentsByExternalIds(externalIds: readonly string[]) { const uniqueExternalIDs = [...new Set(externalIds)]; try { const docs = await getIssuedDocumentsByExternalIds.run( { externalIds: uniqueExternalIDs }, this.db, ); return externalIds.map(id => docs.find(doc => doc.external_id === id)); } catch (e) { console.error(e); return externalIds.map(() => null); } } public getIssuedDocumentsByExternalIdLoader = new DataLoader((externalIds: readonly string[]) => this.batchIssuedDocumentsByExternalIds(externalIds), ); private async batchIssuedDocumentsByClientIds(clientIds: readonly string[]) { const uniqueClientIDs = [...new Set(clientIds)]; try { const docs = await getIssuedDocumentsByClientIds.run({ clientIds: uniqueClientIDs }, this.db); return clientIds.map(id => docs.filter(doc => { this.getIssuedDocumentsByIdLoader.prime(doc.id, doc); return doc.creditor_id === id || doc.debtor_id === id; }), ); } catch (e) { const message = 'Error fetching issued documents by client IDs'; console.error(message, e); throw new Error(message, { cause: e }); } } public getIssuedDocumentsByClientIdLoader = new DataLoader( (clientIds: readonly string[]) => this.batchIssuedDocumentsByClientIds(clientIds), { cache: false, }, ); private async batchIssuedDocumentsStatusByChargeIds(chargeIds: readonly string[]) { const uniqueClientIDs = [...new Set(chargeIds)]; try { const statuses = await getIssuedDocumentsStatusByChargeIds.run( { chargeIds: uniqueClientIDs }, this.db, ); return chargeIds.map(id => statuses.find(status => { return status.charge_id === id; }), ); } catch (e) { const message = 'Error fetching issued documents status by charge IDs'; console.error(message, e); throw new Error(message, { cause: e }); } } public getIssuedDocumentsStatusByChargeIdLoader = new DataLoader( (chargeIds: readonly string[]) => this.batchIssuedDocumentsStatusByChargeIds(chargeIds), { cache: false, }, ); public async getIssuedDocumentsByType(params: IGetIssuedDocumentsByTypeParams) { return getIssuedDocumentsByType.run(params, this.db).then(docs => { docs.map(doc => { this.getIssuedDocumentsByIdLoader.prime(doc.id, doc); }); return docs; }); } public async updateIssuedDocument(params: IUpdateIssuedDocumentParams) { if (params.documentId) { this.invalidateById(params.documentId); } return updateIssuedDocument.run(params, this.db); } public async updateIssuedDocumentByExternalId(params: IUpdateIssuedDocumentByExternalIdParams) { return updateIssuedDocumentByExternalId.run(params, this.db).then(res => { if (res[0]?.id) { this.invalidateById(res[0].id); } return res; }); } public async deleteIssuedDocument(params: IDeleteIssuedDocumentParams) { if (params.documentId) { this.invalidateById(params.documentId); } return deleteIssuedDocument.run(params, this.db); } public async insertIssuedDocuments(params: IInsertIssuedDocumentsParams) { this.allIssuedDocumentsCache = null; const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext(); const issuedDocumentsWithOwnerId = params.issuedDocuments.map(doc => reassureOwnerIdExists(doc, ownerId), ); return insertIssuedDocuments.run({ issuedDocuments: issuedDocumentsWithOwnerId }, this.db); } public async invalidateById(id: string) { this.getIssuedDocumentsByIdLoader.clear(id); this.allIssuedDocumentsCache = null; this.getIssuedDocumentsStatusByChargeIdLoader.clearAll(); } public clearCache() { this.getIssuedDocumentsByIdLoader.clearAll(); this.getIssuedDocumentsByExternalIdLoader.clearAll(); this.getIssuedDocumentsByClientIdLoader.clearAll(); this.getIssuedDocumentsStatusByChargeIdLoader.clearAll(); this.allIssuedDocumentsCache = null; } }