// Canonical DTO types for provider data normalization // ============================================ // Resource Type // ============================================ export const ResourceType = { SalesInvoices: 'salesinvoices', SupplierInvoices: 'supplierinvoices', Customers: 'customers', Suppliers: 'suppliers', Journals: 'journals', AccountingAccounts: 'accountingaccounts', CompanyInformation: 'companyinformation', AccountingPeriods: 'accountingperiods', FinancialDimensions: 'financialdimensions', BalanceSheet: 'balancesheet', IncomeStatement: 'incomestatement', TrialBalances: 'trialbalances', Payments: 'payments', Attachments: 'attachments', } as const; export type ResourceType = (typeof ResourceType)[keyof typeof ResourceType]; // ============================================ // Common // ============================================ export interface AmountType { value: number; currencyCode: string; } export interface PostalAddress { streetName?: string; additionalStreetName?: string; buildingNumber?: string; cityName?: string; postalZone?: string; countrySubentity?: string; countryCode?: string; } export interface Contact { name?: string; telephone?: string; email?: string; website?: string; } export interface PartyIdentification { id: string; schemeId?: string; } export interface PartyLegalEntity { registrationName: string; companyId?: string; companyIdSchemeId?: string; } export interface PartyDto { name: string; identifications: PartyIdentification[]; postalAddress?: PostalAddress; legalEntity?: PartyLegalEntity; contact?: Contact; } export interface FinancialDimensionRef { dimensionId: string; dimensionValueId: string; name?: string; } export interface AllowanceChargeDto { chargeIndicator: boolean; reason?: string; amount: AmountType; taxPercent?: number; } export interface TaxTotalDto { taxAmount: AmountType; taxSubtotals?: TaxSubtotalDto[]; } export interface TaxSubtotalDto { taxableAmount: AmountType; taxAmount: AmountType; taxCategory?: string; percent?: number; } export interface PaginatedResponse { data: T[]; page: number; pageSize: number; totalCount: number; hasMore: boolean; } /** * The verifikat that booked the invoice in the SOURCE system, as the provider * reports it ("A329"). Optional: only providers that expose it (Visma * eAccounting, Fortnox) set it, and only on booked invoices. The migration * uses it to link the imported invoice to the SIE-imported registration * voucher; see lib/providers/source-voucher.ts for the parsing rules. */ export interface SourceVoucherRefDto { series: string | null; number: number; } // ============================================ // Sales Invoice // ============================================ export type InvoiceStatusCode = 'draft' | 'sent' | 'booked' | 'paid' | 'overdue' | 'cancelled' | 'credited'; export interface LegalMonetaryTotalDto { /** * Sum of the line amounts, excluding VAT. * * Optional because several providers omit it from their list payloads * (Fortnox `Net`, Briox `net_amount`) and one never exposes it at all. * Absent means "the net was not established", NOT "the net equals the * gross": mappers must leave it undefined rather than fall back to * `payableAmount`, which silently turns every such invoice into a 0 kr VAT * record that still balances and so goes unnoticed. */ lineExtensionAmount?: AmountType; taxExclusiveAmount?: AmountType; taxInclusiveAmount?: AmountType; allowanceTotalAmount?: AmountType; chargeTotalAmount?: AmountType; payableRoundingAmount?: AmountType; payableAmount: AmountType; } export interface PaymentStatusDto { paid: boolean; balance: AmountType; lastPaymentDate?: string; } export interface SalesInvoiceLineDto { id: string; description?: string; quantity?: number; unitCode?: string; unitPrice?: AmountType; lineExtensionAmount: AmountType; taxPercent?: number; taxAmount?: AmountType; accountNumber?: string; itemName?: string; articleNumber?: string; financialDimensions?: FinancialDimensionRef[]; } export interface SalesInvoiceDto { id: string; invoiceNumber: string; issueDate: string; dueDate?: string; deliveryDate?: string; invoiceTypeCode?: string; currencyCode: string; status: InvoiceStatusCode; supplier: PartyDto; customer: PartyDto; lines: SalesInvoiceLineDto[]; allowanceCharges?: AllowanceChargeDto[]; taxTotal?: TaxTotalDto; legalMonetaryTotal: LegalMonetaryTotalDto; paymentStatus: PaymentStatusDto; paymentTerms?: string; note?: string; buyerReference?: string; orderReference?: string; financialDimensions?: FinancialDimensionRef[]; sourceVoucher?: SourceVoucherRefDto; createdAt?: string; updatedAt?: string; _raw?: Record; } // ============================================ // Supplier Invoice // ============================================ export interface SupplierInvoiceLineDto { id: string; description?: string; quantity?: number; unitCode?: string; unitPrice?: AmountType; lineExtensionAmount: AmountType; taxPercent?: number; taxAmount?: AmountType; accountNumber?: string; itemName?: string; articleNumber?: string; financialDimensions?: FinancialDimensionRef[]; } export interface SupplierInvoiceDto { id: string; invoiceNumber: string; issueDate: string; dueDate?: string; deliveryDate?: string; invoiceTypeCode?: string; currencyCode: string; status: InvoiceStatusCode; supplier: PartyDto; buyer: PartyDto; lines: SupplierInvoiceLineDto[]; allowanceCharges?: AllowanceChargeDto[]; taxTotal?: TaxTotalDto; legalMonetaryTotal: LegalMonetaryTotalDto; paymentStatus: PaymentStatusDto; paymentTerms?: string; note?: string; ocrNumber?: string; financialDimensions?: FinancialDimensionRef[]; sourceVoucher?: SourceVoucherRefDto; createdAt?: string; updatedAt?: string; _raw?: Record; } // ============================================ // Customer // ============================================ export type CustomerType = 'company' | 'private'; export interface CustomerDto { id: string; customerNumber: string; type?: CustomerType; party: PartyDto; invoiceEmailCcAddresses?: string[]; invoiceEmailBccAddresses?: string[]; deliveryAddresses?: PostalAddress[]; financialDimensions?: FinancialDimensionRef[]; active: boolean; vatNumber?: string; defaultPaymentTermsDays?: number; note?: string; createdAt?: string; updatedAt?: string; _raw?: Record; } // ============================================ // Supplier // ============================================ export interface SupplierDto { id: string; supplierNumber: string; party: PartyDto; deliveryAddresses?: PostalAddress[]; financialDimensions?: FinancialDimensionRef[]; active: boolean; vatNumber?: string; bankAccount?: string; bankGiro?: string; plusGiro?: string; defaultPaymentTermsDays?: number; note?: string; createdAt?: string; updatedAt?: string; _raw?: Record; } // ============================================ // Journal // ============================================ export interface AccountingEntryDto { accountNumber: string; accountName?: string; debit: number; credit: number; transactionDate?: string; description?: string; financialDimensions?: FinancialDimensionRef[]; } export interface AccountingSeriesDto { id: string; description?: string; } export interface JournalDto { id: string; journalNumber: string; series?: AccountingSeriesDto; description?: string; registrationDate: string; fiscalYear?: number; entries: AccountingEntryDto[]; totalDebit?: AmountType; totalCredit?: AmountType; createdAt?: string; updatedAt?: string; _raw?: Record; } // ============================================ // Accounting Account // ============================================ export type AccountType = 'asset' | 'liability' | 'equity' | 'revenue' | 'expense' | 'other'; export interface AccountingAccountDto { accountNumber: string; name: string; description?: string; type?: AccountType; vatCode?: string; active: boolean; balanceBroughtForward?: number; balanceCarriedForward?: number; sruCode?: string; createdAt?: string; updatedAt?: string; _raw?: Record; } // ============================================ // Company Information // ============================================ export interface CompanyInformationDto { companyName: string; organizationNumber?: string; legalEntity?: PartyLegalEntity; address?: PostalAddress; contact?: Contact; vatNumber?: string; fiscalYearStart?: string; baseCurrency?: string; _raw?: Record; } // ============================================ // Payment // ============================================ export type PaymentMethodCode = 'bank_transfer' | 'card' | 'cash' | 'autogiro' | 'bankgiro' | 'plusgiro' | 'swish' | 'other'; export interface PaymentDto { id: string; paymentNumber?: string; invoiceId: string; paymentDate: string; amount: AmountType; paymentMethod?: PaymentMethodCode; reference?: string; note?: string; createdAt?: string; updatedAt?: string; _raw?: Record; }