feat: wire Zod validation into API routes, improve types and components
- Add 8 new Zod schemas (UpdateCustomer, UpdateSupplier, UpdateSupplierInvoice, UpdateAccount, BankUnlink, RunReconciliation, CorrectJournalEntry, EvaluateMappingRules) and wire validateBody() into 24 JSON-body API routes - Remove redundant manual validation checks replaced by Zod - Add comprehensive schema tests (222 tests) - Improve type definitions in types/index.ts with expanded interfaces - Refactor extension types (push-notifications, receipt-ocr) for cleaner imports - Update transaction components (BatchCategorySelector, SwipeCategorizationView, QuickReviewDialog, VatTreatmentSelect) and invoice inbox workspace - Add invoice-inbox utilities and type decoupling tests - Fix NE-bilaga, SRU export, and invoice PDF template type usage - Update CLAUDE.md with expanded architecture documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
cfc4cb6849
commit
acb85edf4a
+226
-16
@@ -1175,9 +1175,62 @@ export interface CreateDeadlineInput {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Push Notification Types (canonical source: extensions/push-notifications/types.ts)
|
||||
// Push Notification Types
|
||||
// ============================================================
|
||||
export type { PushSubscription, NotificationSettings, NotificationType, NotificationLog } from '@/extensions/general/push-notifications/types'
|
||||
|
||||
// Push subscription for Web Push API
|
||||
export interface PushSubscription {
|
||||
id: string
|
||||
user_id: string
|
||||
endpoint: string
|
||||
p256dh: string
|
||||
auth: string
|
||||
user_agent: string | null
|
||||
is_active: boolean
|
||||
last_used_at: string | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
// Notification settings per user
|
||||
export interface NotificationSettings {
|
||||
id: string
|
||||
user_id: string
|
||||
tax_deadlines_enabled: boolean
|
||||
invoice_reminders_enabled: boolean
|
||||
quiet_start: string // time format "HH:MM"
|
||||
quiet_end: string // time format "HH:MM"
|
||||
email_enabled: boolean
|
||||
push_enabled: boolean
|
||||
period_locked_enabled: boolean
|
||||
period_year_closed_enabled: boolean
|
||||
invoice_sent_enabled: boolean
|
||||
receipt_extracted_enabled: boolean
|
||||
receipt_matched_enabled: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
// Notification type for logging
|
||||
export type NotificationType =
|
||||
| 'tax_deadline'
|
||||
| 'invoice_due'
|
||||
| 'invoice_overdue'
|
||||
| 'period_locked'
|
||||
| 'period_year_closed'
|
||||
| 'receipt_extracted'
|
||||
| 'receipt_matched'
|
||||
| 'invoice_sent'
|
||||
|
||||
// Notification log entry
|
||||
export interface NotificationLog {
|
||||
id: string
|
||||
user_id: string
|
||||
notification_type: NotificationType
|
||||
reference_id: string
|
||||
days_before: number
|
||||
sent_at: string
|
||||
delivery_status: 'sent' | 'delivered' | 'failed'
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Calendar Feed Types (ICS)
|
||||
@@ -1301,10 +1354,178 @@ export interface InvoiceInboxItem {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Receipt Types (canonical source: extensions/receipt-ocr/types.ts)
|
||||
// Receipt Types
|
||||
// ============================================================
|
||||
export type { ReceiptStatus, Receipt, ReceiptLineItem, ReceiptExtractionResult, ExtractedLineItem, ReceiptMatchCandidate, CreateReceiptInput, ConfirmReceiptInput, ConfirmLineItemInput, ReceiptQueueSummary, CameraQualityFeedback } from '@/extensions/general/receipt-ocr/types'
|
||||
export { RECEIPT_STATUS_LABELS } from '@/extensions/general/receipt-ocr/types'
|
||||
|
||||
// Receipt extraction status
|
||||
export type ReceiptStatus = 'pending' | 'processing' | 'extracted' | 'confirmed' | 'error'
|
||||
|
||||
// Receipt record
|
||||
export interface Receipt {
|
||||
id: string
|
||||
user_id: string
|
||||
|
||||
// Image storage
|
||||
image_url: string
|
||||
image_thumbnail_url: string | null
|
||||
|
||||
// Extraction status
|
||||
status: ReceiptStatus
|
||||
extraction_confidence: number | null
|
||||
|
||||
// Extracted header data
|
||||
merchant_name: string | null
|
||||
merchant_org_number: string | null
|
||||
merchant_vat_number: string | null
|
||||
receipt_date: string | null
|
||||
receipt_time: string | null
|
||||
total_amount: number | null
|
||||
currency: string
|
||||
vat_amount: number | null
|
||||
|
||||
// Special flags
|
||||
is_restaurant: boolean
|
||||
is_systembolaget: boolean
|
||||
is_foreign_merchant: boolean
|
||||
|
||||
// Restaurant representation data
|
||||
representation_persons: number | null
|
||||
representation_purpose: string | null
|
||||
|
||||
// Transaction matching
|
||||
matched_transaction_id: string | null
|
||||
match_confidence: number | null
|
||||
|
||||
// Raw extraction data
|
||||
raw_extraction: ReceiptExtractionResult | null
|
||||
|
||||
created_at: string
|
||||
updated_at: string
|
||||
|
||||
// Relations (populated when fetched)
|
||||
line_items?: ReceiptLineItem[]
|
||||
matched_transaction?: Transaction
|
||||
}
|
||||
|
||||
// Receipt line item record
|
||||
export interface ReceiptLineItem {
|
||||
id: string
|
||||
receipt_id: string
|
||||
|
||||
// Extracted data
|
||||
description: string
|
||||
quantity: number
|
||||
unit_price: number | null
|
||||
line_total: number
|
||||
vat_rate: number | null
|
||||
vat_amount: number | null
|
||||
|
||||
// Classification
|
||||
is_business: boolean | null
|
||||
category: TransactionCategory | null
|
||||
bas_account: string | null
|
||||
|
||||
// Confidence
|
||||
extraction_confidence: number | null
|
||||
suggested_category: string | null
|
||||
|
||||
sort_order: number
|
||||
created_at: string
|
||||
}
|
||||
|
||||
// AI extraction result from Claude Vision
|
||||
export interface ReceiptExtractionResult {
|
||||
merchant: {
|
||||
name: string | null
|
||||
orgNumber: string | null
|
||||
vatNumber: string | null
|
||||
isForeign: boolean
|
||||
}
|
||||
receipt: {
|
||||
date: string | null
|
||||
time: string | null
|
||||
currency: string
|
||||
}
|
||||
lineItems: ExtractedLineItem[]
|
||||
totals: {
|
||||
subtotal: number | null
|
||||
vatAmount: number | null
|
||||
total: number | null
|
||||
}
|
||||
flags: {
|
||||
isRestaurant: boolean
|
||||
isSystembolaget: boolean
|
||||
isForeignMerchant: boolean
|
||||
}
|
||||
confidence: number
|
||||
}
|
||||
|
||||
// Extracted line item from AI
|
||||
export interface ExtractedLineItem {
|
||||
description: string
|
||||
quantity: number
|
||||
unitPrice: number | null
|
||||
lineTotal: number
|
||||
vatRate: number | null
|
||||
suggestedCategory: string | null
|
||||
confidence?: number
|
||||
}
|
||||
|
||||
// Match candidate for receipt-to-transaction matching
|
||||
export interface ReceiptMatchCandidate {
|
||||
transaction: Transaction
|
||||
confidence: number
|
||||
matchReasons: string[]
|
||||
dateVariance: number
|
||||
amountVariance: number
|
||||
}
|
||||
|
||||
// Input for creating a receipt
|
||||
export interface CreateReceiptInput {
|
||||
image_url: string
|
||||
image_thumbnail_url?: string
|
||||
}
|
||||
|
||||
// Input for confirming receipt line items
|
||||
export interface ConfirmReceiptInput {
|
||||
line_items: ConfirmLineItemInput[]
|
||||
matched_transaction_id?: string
|
||||
representation_persons?: number
|
||||
representation_purpose?: string
|
||||
}
|
||||
|
||||
export interface ConfirmLineItemInput {
|
||||
id: string
|
||||
is_business: boolean
|
||||
category?: TransactionCategory
|
||||
bas_account?: string
|
||||
}
|
||||
|
||||
// Receipt queue summary
|
||||
export interface ReceiptQueueSummary {
|
||||
unmatched_receipts_count: number
|
||||
unmatched_transactions_count: number
|
||||
pending_review_count: number
|
||||
streak_count: number
|
||||
}
|
||||
|
||||
// Camera quality feedback
|
||||
export interface CameraQualityFeedback {
|
||||
lightingOk: boolean
|
||||
distanceOk: boolean
|
||||
focusOk: boolean
|
||||
readyToCapture: boolean
|
||||
message?: string
|
||||
}
|
||||
|
||||
// Swedish labels for receipt status
|
||||
export const RECEIPT_STATUS_LABELS: Record<ReceiptStatus, string> = {
|
||||
pending: 'Väntar',
|
||||
processing: 'Analyserar',
|
||||
extracted: 'Extraherat',
|
||||
confirmed: 'Bekräftat',
|
||||
error: 'Fel'
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// VAT Declaration Types (Momsdeklaration)
|
||||
@@ -1377,17 +1598,6 @@ export interface VatDeclarationRequest {
|
||||
period: number
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// NE Declaration Types (canonical source: extensions/ne-bilaga/types.ts)
|
||||
// ============================================================
|
||||
export type { NEDeclarationRutor, NEAccountMapping, NEDeclaration, SRURecord, SRUFile } from '@/extensions/ne-bilaga/types'
|
||||
export { NE_RUTA_LABELS } from '@/extensions/ne-bilaga/types'
|
||||
|
||||
// ============================================================
|
||||
// SRU Export Types (canonical source: extensions/sru-export/types.ts)
|
||||
// ============================================================
|
||||
export type { SRUExportResult, SRUCoverageStats } from '@/extensions/sru-export/types'
|
||||
|
||||
// Labels for VAT rutor
|
||||
export const VAT_RUTA_LABELS: Record<keyof VatDeclarationRutor, string> = {
|
||||
ruta05: 'Utgående moms 25%',
|
||||
|
||||
Reference in New Issue
Block a user