UNPKG

@accounter/server

Version:
430 lines • 14.1 kB
import { __decorate, __metadata } from "tslib"; 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'; const getAllDocuments = sql ` SELECT * FROM accounter_schema.documents ORDER BY created_at DESC; `; const getDocumentsByChargeId = sql ` SELECT * FROM accounter_schema.documents WHERE charge_id in $$chargeIds ORDER BY created_at DESC; `; const getDocumentsByIds = sql ` SELECT * FROM accounter_schema.documents WHERE id IN $$Ids; `; const getDocumentsByBusinessIds = sql ` SELECT * FROM accounter_schema.documents WHERE debtor_id IN $$Ids OR creditor_id IN $$Ids; `; const getDocumentsByHashes = sql ` SELECT * FROM accounter_schema.documents WHERE file_hash IN $$hashes; `; const getDocumentsByMissingRequiredInfo = sql ` SELECT * FROM accounter_schema.documents WHERE charge_id IS NOT NULL AND (image_url IS NULL AND file_url IS NULL) -- missing link OR (type <> 'OTHER' AND (serial_number IS NULL OR date IS NULL OR total_amount IS NULL OR currency_code IS NULL OR vat_amount IS NULL OR debtor_id IS NULL OR creditor_id IS NULL)); `; const updateDocument = sql ` UPDATE accounter_schema.documents SET charge_id = CASE WHEN $chargeId='00000000-0000-0000-0000-000000000000' THEN NULL ELSE COALESCE( $chargeId::UUID, charge_id, NULL ) END, currency_code = COALESCE( $currencyCode, currency_code, NULL ), date = COALESCE( $date, date, NULL ), file_url = COALESCE( $fileUrl, file_url, NULL ), id = COALESCE( id, id, NULL ), image_url = COALESCE( $imageUrl, image_url, NULL ), updated_at = NOW(), serial_number = COALESCE( $serialNumber, serial_number, NULL ), total_amount = COALESCE( $totalAmount, total_amount, NULL ), type = COALESCE( $type, type, NULL ), vat_amount = COALESCE( $vatAmount, vat_amount, NULL ), is_reviewed = COALESCE( $isReviewed, is_reviewed ), creditor_id = COALESCE( $creditorId, creditor_id ), debtor_id = COALESCE( $debtorId, debtor_id ), no_vat_amount = COALESCE( $noVatAmount, no_vat_amount ), vat_report_date_override = COALESCE( $vatReportDateOverride, vat_report_date_override ), allocation_number = COALESCE( $allocationNumber, allocation_number ), exchange_rate_override = COALESCE( $exchangeRateOverride, exchange_rate_override ), description = COALESCE( $description, description ), remarks = COALESCE( $remarks, remarks ) WHERE id = $documentId RETURNING *; `; const deleteDocument = sql ` DELETE FROM accounter_schema.documents WHERE id = $documentId RETURNING id; `; const insertDocuments = sql ` INSERT INTO accounter_schema.documents ( image_url, file_url, type, serial_number, date, total_amount, currency_code, vat_amount, charge_id, vat_report_date_override, no_vat_amount, creditor_id, debtor_id, allocation_number, exchange_rate_override, file_hash, description, remarks, owner_id ) VALUES $$documents( image, file, documentType, serialNumber, date, amount, currencyCode, vat, chargeId, vatReportDateOverride, noVatAmount, creditorId, debtorId, allocationNumber, exchangeRateOverride, fileHash, description, remarks, ownerId ) RETURNING *;`; const getDocumentsByFilters = sql ` SELECT * FROM accounter_schema.documents WHERE ($isIDs = 0 OR id IN $$IDs) AND ($fromVatDate ::TEXT IS NULL OR COALESCE(vat_report_date_override ,date)::TEXT::DATE >= date_trunc('day', $fromVatDate ::DATE)) AND ($toVatDate ::TEXT IS NULL OR COALESCE(vat_report_date_override ,date)::TEXT::DATE <= date_trunc('day', $toVatDate ::DATE)) AND ($isBusinessIDs = 0 OR debtor_id IN $$businessIDs OR creditor_id IN $$businessIDs) ORDER BY created_at DESC; `; const getDocumentsByExtendedFilters = sql ` SELECT * FROM accounter_schema.documents WHERE ($isIDs = 0 OR id IN $$IDs) AND ($fromVatDate ::TEXT IS NULL OR COALESCE(vat_report_date_override ,date)::TEXT::DATE >= date_trunc('day', $fromVatDate ::DATE)) AND ($toVatDate ::TEXT IS NULL OR COALESCE(vat_report_date_override ,date)::TEXT::DATE <= date_trunc('day', $toVatDate ::DATE)) AND ($fromDate ::TEXT IS NULL OR date::TEXT::DATE >= date_trunc('day', $fromDate ::DATE)) AND ($toDate ::TEXT IS NULL OR date::TEXT::DATE <= date_trunc('day', $toDate ::DATE)) AND ($isBusinessIDs = 0 OR debtor_id IN $$businessIDs OR creditor_id IN $$businessIDs) AND ($isOwnerIDs = 0 OR owner_id IN $$ownerIDs) AND ($isUnmatched = 0 OR NOT EXISTS ( SELECT 1 FROM accounter_schema.transactions t WHERE t.charge_id = charge_id )) ORDER BY created_at DESC; `; const replaceDocumentsChargeId = sql ` UPDATE accounter_schema.documents SET charge_id = $assertChargeID WHERE charge_id = $replaceChargeID RETURNING id; `; let DocumentsProvider = class DocumentsProvider { db; adminContextProvider; constructor(db, adminContextProvider) { this.db = db; this.adminContextProvider = adminContextProvider; } allDocumentsCache = null; async getAllDocuments(params) { if (this.allDocumentsCache) { return this.allDocumentsCache; } this.allDocumentsCache = getAllDocuments.run(params, this.db).then(docs => { docs.map(doc => { this.getDocumentsByIdLoader.prime(doc.id, doc); }); return docs; }); return this.allDocumentsCache; } async batchDocumentsByChargeIds(chargeIds) { const uniqueIDs = [...new Set(chargeIds)]; try { const docs = await getDocumentsByChargeId.run({ chargeIds: uniqueIDs }, this.db); return chargeIds.map(id => docs.filter(doc => { this.getDocumentsByIdLoader.prime(doc.id, doc); return doc.charge_id === id; })); } catch (e) { console.error(e); return chargeIds.map(() => []); } } getDocumentsByChargeIdLoader = new DataLoader((keys) => this.batchDocumentsByChargeIds(keys)); getDocumentsByFilters(params) { const isIDs = !!params?.IDs?.filter(Boolean).length; const isBusinessIDs = !!params?.businessIDs?.filter(Boolean).length; const fullParams = { isIDs: isIDs ? 1 : 0, isBusinessIDs: isBusinessIDs ? 1 : 0, fromVatDate: null, toVatDate: null, ...params, IDs: isIDs ? params.IDs : [null], businessIDs: isBusinessIDs ? params.businessIDs : [null], }; return getDocumentsByFilters.run(fullParams, this.db).then(docs => { docs.map(doc => { this.getDocumentsByIdLoader.prime(doc.id, doc); }); return docs; }); } getDocumentsByExtendedFilters(params) { const isIDs = !!params?.IDs?.filter(Boolean).length; const isBusinessIDs = !!params?.businessIDs?.filter(Boolean).length; const isOwnerIDs = !!params?.ownerIDs?.filter(Boolean).length; const fullParams = { isIDs: isIDs ? 1 : 0, isBusinessIDs: isBusinessIDs ? 1 : 0, isOwnerIDs: isOwnerIDs ? 1 : 0, fromVatDate: null, toVatDate: null, ...params, isUnmatched: params.unmatched ? 1 : 0, IDs: isIDs ? params.IDs : [null], businessIDs: isBusinessIDs ? params.businessIDs : [null], ownerIDs: isOwnerIDs ? params.ownerIDs : [null], }; return getDocumentsByExtendedFilters.run(fullParams, this.db); } async batchDocumentsByIds(ids) { const uniqueIDs = [...new Set(ids)]; try { const docs = await getDocumentsByIds.run({ Ids: uniqueIDs }, this.db); return ids.map(id => docs.find(doc => doc.id === id)); } catch (e) { console.error(e); return ids.map(() => null); } } getDocumentsByIdLoader = new DataLoader((keys) => this.batchDocumentsByIds(keys)); async batchDocumentsByBusinessIds(businessIds) { const uniqueIDs = [...new Set(businessIds)]; try { const docs = await getDocumentsByBusinessIds.run({ Ids: uniqueIDs }, this.db); docs.map(doc => { this.getDocumentsByIdLoader.prime(doc.id, doc); }); return businessIds.map(id => docs.filter(doc => doc.creditor_id === id || doc.debtor_id === id)); } catch (e) { console.error(e); return businessIds.map(() => null); } } getDocumentsByBusinessIdLoader = new DataLoader((businessIds) => this.batchDocumentsByBusinessIds(businessIds)); async batchDocumentsByHash(hashes) { const uniqueHashes = [...new Set(hashes)]; try { const docs = await getDocumentsByHashes.run({ hashes: uniqueHashes.map(hash => hash.toString()) }, this.db); docs.map(doc => { this.getDocumentsByIdLoader.prime(doc.id, doc); }); return hashes.map(hash => docs.find(doc => doc.file_hash === hash.toString())); } catch (e) { console.error(e); return hashes.map(() => null); } } getDocumentByHash = new DataLoader((hashes) => this.batchDocumentsByHash(hashes)); async getDocumentsByMissingRequiredInfo() { return getDocumentsByMissingRequiredInfo.run(undefined, this.db); } async updateDocument(params) { if (params.documentId) { const document = await this.getDocumentsByIdLoader.load(params.documentId); if (document?.charge_id) { this.getDocumentsByChargeIdLoader.clear(document.charge_id); } this.invalidateById(params.documentId); } const totalAmount = params.totalAmount == null ? undefined : Math.abs(params.totalAmount); // ensure amount is positive, as the sign is determined by debtor/creditor return updateDocument.run({ ...params, totalAmount }, this.db); } async deleteDocument(params) { if (params.documentId) { const document = await this.getDocumentsByIdLoader.load(params.documentId); if (document?.charge_id) { this.getDocumentsByChargeIdLoader.clear(document.charge_id); } this.invalidateById(params.documentId); } return deleteDocument.run(params, this.db); } async insertDocuments(params, dbConnection) { if (params.documents.length) { params.documents.map(doc => { if (doc.chargeId) this.invalidateByChargeId(doc.chargeId); }); } const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext(); const documentsWithOwnerId = params.documents.map(doc => reassureOwnerIdExists({ ...doc, amount: doc.amount == null ? undefined : Math.abs(doc.amount) }, // ensure amount is positive, as the sign is determined by debtor/creditor ownerId)); return insertDocuments.run({ documents: documentsWithOwnerId }, dbConnection ?? this.db); } async replaceDocumentsChargeId(params) { if (params.assertChargeID) { this.invalidateByChargeId(params.assertChargeID); } if (params.replaceChargeID) { this.invalidateByChargeId(params.replaceChargeID); } return replaceDocumentsChargeId.run(params, this.db); } async invalidateById(id) { const document = await this.getDocumentsByIdLoader.load(id); if (document) { if (document.charge_id) this.getDocumentsByChargeIdLoader.clear(document.charge_id); if (document.file_hash) this.getDocumentByHash.clear(Number(document.file_hash)); if (document.debtor_id) this.getDocumentsByBusinessIdLoader.clear(document.debtor_id); if (document.creditor_id) this.getDocumentsByBusinessIdLoader.clear(document.creditor_id); } this.getDocumentsByIdLoader.clear(id); this.allDocumentsCache = null; } async invalidateByChargeId(chargeId) { const documents = await this.getDocumentsByChargeIdLoader.load(chargeId); documents.map(doc => { this.getDocumentsByIdLoader.clear(doc.id); if (doc.debtor_id) this.getDocumentsByBusinessIdLoader.clear(doc.debtor_id); if (doc.creditor_id) this.getDocumentsByBusinessIdLoader.clear(doc.creditor_id); }); this.getDocumentsByChargeIdLoader.clear(chargeId); this.allDocumentsCache = null; } clearCache() { this.getDocumentsByIdLoader.clearAll(); this.getDocumentsByChargeIdLoader.clearAll(); this.getDocumentsByBusinessIdLoader.clearAll(); this.getDocumentByHash.clearAll(); this.allDocumentsCache = null; } }; DocumentsProvider = __decorate([ Injectable({ scope: Scope.Operation, global: true, }), __metadata("design:paramtypes", [TenantAwareDBClient, AdminContextProvider]) ], DocumentsProvider); export { DocumentsProvider }; //# sourceMappingURL=documents.provider.js.map