@accounter/server
Version:
Accounter GraphQL server
430 lines • 14.1 kB
JavaScript
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