Files
pn-new-crm/src/lib/services/receipt-scanner.ts
Matt 67d7e6e3d5
Some checks failed
Build & Push Docker Images / build-and-push (push) Has been cancelled
Build & Push Docker Images / deploy (push) Has been cancelled
Build & Push Docker Images / lint (push) Has been cancelled
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00

56 lines
1.5 KiB
TypeScript

import OpenAI from 'openai';
import { logger } from '@/lib/logger';
const openai = new OpenAI(); // uses OPENAI_API_KEY from env
interface ScanResult {
establishment: string | null;
date: string | null;
amount: number | null;
currency: string | null;
lineItems: Array<{ description: string; amount: number }>;
confidence: number;
}
export async function scanReceipt(
imageBuffer: Buffer,
mimeType: string,
): Promise<ScanResult> {
try {
const base64 = imageBuffer.toString('base64');
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: 'Extract receipt data as JSON: { establishment, date (ISO), amount (number), currency (3-letter code), lineItems: [{ description, amount }], confidence (0-1) }. Return ONLY valid JSON.',
},
{
type: 'image_url',
image_url: { url: `data:${mimeType};base64,${base64}` },
},
],
},
],
max_tokens: 1000,
});
const content = response.choices[0]?.message?.content ?? '{}';
const cleaned = content.replace(/```json\n?|\n?```/g, '').trim();
return JSON.parse(cleaned) as ScanResult;
} catch (err) {
logger.error({ err }, 'Receipt scan failed');
return {
establishment: null,
date: null,
amount: null,
currency: null,
lineItems: [],
confidence: 0,
};
}
}