UNPKG

@accounter/server

Version:
137 lines 5.44 kB
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