@accounter/server
Version:
Accounter GraphQL server
137 lines • 5.44 kB
JavaScript
import { DocumentType } from '../../../shared/enums.js';
import { hashStringToInt } from '../../../shared/helpers/index.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { AnthropicProvider } from '../../app-providers/anthropic.js';
import { CloudinaryProvider } from '../../app-providers/cloudinary.js';
const toBase64 = async (file) => {
const base64string = Buffer.from(await file.arrayBuffer()).toString('base64');
return 'data:' + file.type + ';base64,' + base64string;
};
export const uploadToCloudinary = async (injector, file) => {
const base64string = await toBase64(file).catch(err => {
throw new Error(`Failed to convert file to base64: ${err.message}`);
});
try {
return injector.get(CloudinaryProvider).uploadInvoiceToCloudinary(base64string);
}
catch (e) {
const message = 'Error on uploading file to cloudinary';
console.error(`${message}: ${e}`);
throw new Error(message, { cause: e });
}
};
export async function getOcrData(injector, file, isSensitive = true) {
const validateNumber = (value) => {
return typeof value === 'number' && !Number.isNaN(value) ? value : undefined;
};
const validateDate = (value) => {
if (!value)
return undefined;
const date = new Date(value);
return Number.isNaN(date.getTime()) ? undefined : date;
};
if (isSensitive) {
return {
documentType: DocumentType.Unprocessed,
};
}
const draft = await injector.get(AnthropicProvider).extractInvoiceDetails(file);
if (!draft) {
throw new Error('No data returned from Anthropic OCR');
}
let isOwnerIssuer = undefined;
if (draft.recipient?.toLocaleLowerCase().includes('the guild')) {
isOwnerIssuer = false;
}
if (draft.issuer?.toLocaleLowerCase().includes('the guild')) {
isOwnerIssuer = true;
}
return {
isOwnerIssuer,
documentType: draft.type ?? DocumentType.Unprocessed,
serial: draft.referenceCode ?? undefined,
date: validateDate(draft.date ?? undefined),
amount: validateNumber(draft.fullAmount),
currency: draft.currency,
vat: validateNumber(draft.vatAmount),
allocationNumber: draft.allocationNumber ?? undefined,
description: draft.description ?? undefined,
remarks: draft.issuer,
};
}
async function getHashFromFile(file) {
return hashStringToInt(await file.text());
}
function figureOutSides(documentType, ownerId, isOwnerIssuer, counterPartyId) {
if (documentType === DocumentType.Unprocessed || documentType === DocumentType.Other) {
return {};
}
let res = {
creditorId: counterPartyId,
debtorId: ownerId,
};
if (isOwnerIssuer === true) {
res = {
creditorId: res.debtorId,
debtorId: res.creditorId,
};
}
if (documentType === DocumentType.CreditInvoice) {
res = {
creditorId: res.debtorId,
debtorId: res.creditorId,
};
}
return res;
}
export function getDocumentFromUrlsAndOcrData(fileUrl, imageUrl, ocrData, adminBusinessId, chargeId, fileHash) {
const sides = figureOutSides(ocrData.documentType, adminBusinessId, ocrData.isOwnerIssuer, ocrData.counterpartyId);
const newDocument = {
ownerId: adminBusinessId,
image: imageUrl ?? null,
file: fileUrl ?? null,
documentType: ocrData.documentType,
serialNumber: ocrData.serial ?? null,
date: ocrData.date ?? null,
amount: ocrData.amount ?? null,
currencyCode: ocrData.currency ?? null,
vat: ocrData.vat ?? null,
chargeId: chargeId ?? null,
vatReportDateOverride: null,
noVatAmount: null,
allocationNumber: ocrData.allocationNumber ?? null,
exchangeRateOverride: null,
fileHash: fileHash?.toString() ?? null,
description: ocrData.description ?? null,
remarks: ocrData.remarks ?? null,
creditorId: sides.creditorId ?? null,
debtorId: sides.debtorId ?? null,
};
return newDocument;
}
export async function getDocumentFromFile(injector, file, chargeId, isSensitive, counterPartyId, hash) {
try {
const { ownerId } = await injector.get(AdminContextProvider).getVerifiedAdminContext();
// Buffer the file to allow multiple reads from a stream
const buffer = await file.arrayBuffer();
const multiReadableFile = new Blob([buffer], { type: file.type });
const [{ fileUrl, imageUrl }, ocrData, fileHash] = await Promise.all([
uploadToCloudinary(injector, multiReadableFile),
getOcrData(injector, multiReadableFile, isSensitive),
hash ? Promise.resolve(hash) : getHashFromFile(multiReadableFile),
]);
if (!ocrData) {
throw new Error('No data returned from Green Invoice');
}
if (counterPartyId) {
ocrData.counterpartyId = counterPartyId;
}
return getDocumentFromUrlsAndOcrData(fileUrl, imageUrl, ocrData, ownerId, chargeId, fileHash);
}
catch (e) {
const message = 'Error extracting document data from file';
console.error(`${message}: ${e}`);
throw new Error(message, { cause: e });
}
}
//# sourceMappingURL=upload.helper.js.map