3f255ea201
Extract shared vision/document analysis logic into lib/ai (vision-client, document-analyzer, image preprocessing, validation helpers), simplifying invoice-analyzer, receipt-analyzer, and document classifier. Add migration 046 for journal entry reversal/correction link columns (reversed_by_id, reverses_id, correction_of_id) required by storno service. Update extension components and shared UI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
118 lines
2.9 KiB
TypeScript
118 lines
2.9 KiB
TypeScript
/**
|
|
* Validation Helpers for AI Extraction Results
|
|
*
|
|
* Shared validation functions used by receipt-analyzer and invoice-analyzer
|
|
* to sanitize and validate AI-extracted field values.
|
|
*/
|
|
|
|
/**
|
|
* Validate and trim a string value.
|
|
* Returns null for empty/non-string values.
|
|
*/
|
|
export function validateString(value: unknown): string | null {
|
|
if (typeof value === 'string' && value.trim()) {
|
|
return value.trim()
|
|
}
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Validate a numeric value.
|
|
* Handles both number and string inputs, stripping non-numeric characters.
|
|
* Returns null for invalid values.
|
|
*/
|
|
export function validateNumber(value: unknown): number | null {
|
|
if (typeof value === 'number' && !isNaN(value)) {
|
|
return value
|
|
}
|
|
if (typeof value === 'string') {
|
|
const parsed = parseFloat(value.replace(/[^\d.-]/g, ''))
|
|
if (!isNaN(parsed)) {
|
|
return parsed
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Validate and normalize a date to ISO format (YYYY-MM-DD).
|
|
* Returns null for invalid dates.
|
|
*/
|
|
export function validateDate(value: unknown): string | null {
|
|
if (typeof value !== 'string') return null
|
|
|
|
const date = new Date(value)
|
|
if (isNaN(date.getTime())) return null
|
|
|
|
return date.toISOString().split('T')[0]
|
|
}
|
|
|
|
/**
|
|
* Validate and normalize a time value to HH:MM format.
|
|
* Returns null for invalid times.
|
|
*/
|
|
export function validateTime(value: unknown): string | null {
|
|
if (typeof value !== 'string') return null
|
|
|
|
// Match HH:MM or HH:MM:SS
|
|
const match = value.match(/^(\d{2}):(\d{2})(:\d{2})?$/)
|
|
if (match) {
|
|
return `${match[1]}:${match[2]}`
|
|
}
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Validate and normalize a Swedish org number (XXXXXX-XXXX).
|
|
* Swedish org numbers are 10 digits.
|
|
* Returns null for invalid values.
|
|
*/
|
|
export function validateOrgNumber(value: unknown): string | null {
|
|
if (typeof value !== 'string') return null
|
|
|
|
const digits = value.replace(/\D/g, '')
|
|
|
|
// Swedish org numbers are 10 digits
|
|
if (digits.length === 10) {
|
|
return `${digits.slice(0, 6)}-${digits.slice(6)}`
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Validate a Swedish VAT number (SE prefix, at least 12 chars).
|
|
* Returns null for invalid values.
|
|
*/
|
|
export function validateVatNumber(value: unknown): string | null {
|
|
if (typeof value !== 'string') return null
|
|
|
|
const cleaned = value.trim().toUpperCase()
|
|
if (cleaned.startsWith('SE') && cleaned.length >= 12) {
|
|
return cleaned
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Check if an org number is in Swedish format (10 digits).
|
|
*/
|
|
export function isSwedishOrgNumber(value: string): boolean {
|
|
const digits = value.replace(/\D/g, '')
|
|
return digits.length === 10
|
|
}
|
|
|
|
/**
|
|
* Validate a 4-digit BAS account number (1000-9999).
|
|
* Returns null for invalid values.
|
|
*/
|
|
export function validateAccountNumber(value: string | undefined | null): string | null {
|
|
if (!value) return null
|
|
const digits = value.replace(/\D/g, '')
|
|
if (digits.length === 4 && parseInt(digits) >= 1000 && parseInt(digits) <= 9999) {
|
|
return digits
|
|
}
|
|
return null
|
|
}
|