2519 lines
58 KiB
TypeScript
2519 lines
58 KiB
TypeScript
// Entity types
|
|
export type EntityType = 'enskild_firma' | 'aktiebolag' | 'light'
|
|
|
|
// Shadow Ledger types (Light mode)
|
|
export type ShadowLedgerEntryType = 'payout' | 'gift' | 'expense' | 'hobby_income' | 'hobby_expense'
|
|
export type ShadowLedgerSource = 'manual' | 'email_parser' | 'gigapay_sync' | 'bank_match'
|
|
|
|
// Transaction categories
|
|
export type TransactionCategory =
|
|
| 'income_services'
|
|
| 'income_products'
|
|
| 'income_sponsorship'
|
|
| 'income_affiliate'
|
|
| 'income_other'
|
|
| 'expense_equipment'
|
|
| 'expense_software'
|
|
| 'expense_travel'
|
|
| 'expense_office'
|
|
| 'expense_marketing'
|
|
| 'expense_professional_services'
|
|
| 'expense_education'
|
|
| 'expense_bank_fees'
|
|
| 'expense_card_fees'
|
|
| 'expense_currency_exchange'
|
|
| 'expense_other'
|
|
| 'private'
|
|
| 'uncategorized'
|
|
|
|
// Customer types for VAT handling
|
|
export type CustomerType =
|
|
| 'individual' // Swedish private person
|
|
| 'swedish_business' // Swedish company
|
|
| 'eu_business' // EU company (needs VAT validation)
|
|
| 'non_eu_business' // Non-EU company
|
|
|
|
// Invoice status
|
|
export type InvoiceStatus = 'draft' | 'sent' | 'paid' | 'overdue' | 'cancelled' | 'credited'
|
|
|
|
// VAT treatment
|
|
export type VatTreatment =
|
|
| 'standard_25' // 25% Swedish VAT
|
|
| 'reduced_12' // 12% reduced rate
|
|
| 'reduced_6' // 6% reduced rate
|
|
| 'reverse_charge' // EU reverse charge (0%)
|
|
| 'export' // Non-EU export (0%)
|
|
| 'exempt' // VAT exempt
|
|
|
|
// Moms reporting period
|
|
export type MomsPeriod = 'monthly' | 'quarterly' | 'yearly'
|
|
|
|
// Bank connection status
|
|
export type BankConnectionStatus = 'pending' | 'active' | 'expired' | 'revoked' | 'error'
|
|
|
|
// Salary payment status
|
|
export type SalaryPaymentStatus = 'planned' | 'paid' | 'reported'
|
|
|
|
// Currency types
|
|
export type Currency = 'SEK' | 'EUR' | 'USD' | 'GBP' | 'NOK' | 'DKK'
|
|
|
|
// Profile (extends auth.users)
|
|
export interface Profile {
|
|
id: string
|
|
email: string
|
|
full_name: string | null
|
|
avatar_url: string | null
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Company Settings
|
|
export interface CompanySettings {
|
|
id: string
|
|
user_id: string
|
|
|
|
// Entity info
|
|
entity_type: EntityType
|
|
company_name: string | null
|
|
org_number: string | null
|
|
|
|
// Address
|
|
address_line1: string | null
|
|
address_line2: string | null
|
|
postal_code: string | null
|
|
city: string | null
|
|
country: string
|
|
|
|
// Tax registration
|
|
f_skatt: boolean
|
|
vat_registered: boolean
|
|
vat_number: string | null
|
|
moms_period: MomsPeriod | null
|
|
|
|
// Fiscal year
|
|
fiscal_year_start_month: number // 1-12
|
|
|
|
// Preliminary tax
|
|
preliminary_tax_monthly: number | null
|
|
|
|
// Bank details for invoices
|
|
bank_name: string | null
|
|
clearing_number: string | null
|
|
account_number: string | null
|
|
iban: string | null
|
|
bic: string | null
|
|
|
|
// Onboarding
|
|
onboarding_step: number
|
|
onboarding_complete: boolean
|
|
|
|
// Light mode fields
|
|
municipal_tax_rate: number | null
|
|
municipality_code: string | null
|
|
church_tax: boolean
|
|
church_tax_rate: number | null
|
|
church_parish_code: string | null
|
|
umbrella_provider: string | null
|
|
umbrella_fee_percent: number | null
|
|
umbrella_pension_percent: number | null
|
|
umbrella_fee_custom: boolean
|
|
|
|
// Timestamps
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Bank Connection
|
|
export interface BankConnection {
|
|
id: string
|
|
user_id: string
|
|
|
|
bank_name: string
|
|
bank_id: string
|
|
|
|
// Enable Banking specific
|
|
session_id: string | null
|
|
authorization_id: string | null
|
|
|
|
// Account info
|
|
accounts: BankAccount[]
|
|
|
|
// Status
|
|
status: BankConnectionStatus
|
|
|
|
// Consent
|
|
consent_expires_at: string | null
|
|
last_synced_at: string | null
|
|
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface BankAccount {
|
|
uid: string // Enable Banking account UID
|
|
iban: string | null
|
|
name: string | null
|
|
currency: Currency
|
|
balance: number | null
|
|
}
|
|
|
|
// Transaction
|
|
export interface Transaction {
|
|
id: string
|
|
user_id: string
|
|
|
|
// Source
|
|
bank_connection_id: string | null
|
|
external_id: string | null // For deduplication
|
|
|
|
// Details
|
|
date: string
|
|
description: string
|
|
amount: number // Positive = income, negative = expense
|
|
currency: Currency
|
|
|
|
// For non-SEK transactions
|
|
amount_sek: number | null
|
|
exchange_rate: number | null
|
|
exchange_rate_date: string | null
|
|
|
|
// Categorization
|
|
category: TransactionCategory
|
|
is_business: boolean | null // null = uncategorized
|
|
|
|
// Linked invoice (for matching)
|
|
invoice_id: string | null
|
|
|
|
// Potential invoice match (suggested, not confirmed)
|
|
potential_invoice_id: string | null
|
|
|
|
// Bookkeeping
|
|
journal_entry_id: string | null
|
|
mcc_code: number | null
|
|
merchant_name: string | null
|
|
|
|
// Receipt link
|
|
receipt_id: string | null
|
|
|
|
// Notes
|
|
notes: string | null
|
|
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Customer
|
|
export interface Customer {
|
|
id: string
|
|
user_id: string
|
|
|
|
// Basic info
|
|
name: string
|
|
customer_type: CustomerType
|
|
|
|
// Contact
|
|
email: string | null
|
|
phone: string | null
|
|
|
|
// Address
|
|
address_line1: string | null
|
|
address_line2: string | null
|
|
postal_code: string | null
|
|
city: string | null
|
|
country: string
|
|
|
|
// Tax info
|
|
org_number: string | null
|
|
vat_number: string | null
|
|
vat_number_validated: boolean
|
|
vat_number_validated_at: string | null
|
|
|
|
// Payment
|
|
default_payment_terms: number // Days
|
|
|
|
// Campaign-related (new)
|
|
customer_category: CustomerCategory | null
|
|
average_payment_days: number | null
|
|
|
|
// Notes
|
|
notes: string | null
|
|
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Invoice
|
|
export interface Invoice {
|
|
id: string
|
|
user_id: string
|
|
customer_id: string
|
|
|
|
// Invoice number (auto-generated)
|
|
invoice_number: string
|
|
|
|
// Dates
|
|
invoice_date: string
|
|
due_date: string
|
|
|
|
// Status
|
|
status: InvoiceStatus
|
|
|
|
// Currency
|
|
currency: Currency
|
|
|
|
// Exchange rate (if non-SEK)
|
|
exchange_rate: number | null
|
|
exchange_rate_date: string | null
|
|
|
|
// Amounts
|
|
subtotal: number
|
|
subtotal_sek: number | null
|
|
|
|
vat_amount: number
|
|
vat_amount_sek: number | null
|
|
|
|
total: number
|
|
total_sek: number | null
|
|
|
|
// VAT
|
|
vat_treatment: VatTreatment
|
|
vat_rate: number
|
|
moms_ruta: string | null // For Swedish VAT reporting (05, 39, 40, etc.)
|
|
|
|
// Reference
|
|
your_reference: string | null
|
|
our_reference: string | null
|
|
|
|
// Notes
|
|
notes: string | null
|
|
|
|
// Reverse charge text (auto-added for EU B2B)
|
|
reverse_charge_text: string | null
|
|
|
|
// Credit note reference
|
|
credited_invoice_id: string | null
|
|
|
|
// Payment tracking
|
|
paid_at: string | null
|
|
paid_amount: number | null
|
|
|
|
// Campaign linking (new)
|
|
campaign_id: string | null
|
|
payment_status: PaymentStatus | null
|
|
expected_payment_date: string | null
|
|
actual_payment_date: string | null
|
|
|
|
created_at: string
|
|
updated_at: string
|
|
|
|
// Relations (populated when fetched)
|
|
customer?: Customer
|
|
items?: InvoiceItem[]
|
|
campaign?: Campaign
|
|
}
|
|
|
|
// Invoice Item
|
|
export interface InvoiceItem {
|
|
id: string
|
|
invoice_id: string
|
|
|
|
// Order
|
|
sort_order: number
|
|
|
|
// Description
|
|
description: string
|
|
|
|
// Quantity
|
|
quantity: number
|
|
unit: string // 'st', 'tim', 'dag', etc.
|
|
|
|
// Price
|
|
unit_price: number
|
|
|
|
// Calculated
|
|
line_total: number
|
|
|
|
created_at: string
|
|
}
|
|
|
|
// Salary Payment (for AB)
|
|
export interface SalaryPayment {
|
|
id: string
|
|
user_id: string
|
|
|
|
// Payment details
|
|
gross_amount: number
|
|
net_amount: number
|
|
|
|
// Employer costs
|
|
employer_tax: number // Arbetsgivaravgifter (31.42%)
|
|
preliminary_tax: number // Employee preliminary tax
|
|
|
|
// Dates
|
|
payment_date: string
|
|
period_start: string
|
|
period_end: string
|
|
|
|
// Status
|
|
status: SalaryPaymentStatus
|
|
|
|
// Notes
|
|
notes: string | null
|
|
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Tax Rates (reference table)
|
|
export interface TaxRate {
|
|
id: string
|
|
|
|
// Type
|
|
rate_type: 'egenavgifter' | 'bolagsskatt' | 'arbetsgivaravgifter' | 'vat' | 'municipal'
|
|
|
|
// Rate
|
|
rate: number
|
|
|
|
// Validity
|
|
valid_from: string
|
|
valid_to: string | null
|
|
|
|
// Description
|
|
description: string
|
|
}
|
|
|
|
// Form types for creating/updating
|
|
|
|
export interface CreateCustomerInput {
|
|
name: string
|
|
customer_type: CustomerType
|
|
email?: string
|
|
phone?: string
|
|
address_line1?: string
|
|
address_line2?: string
|
|
postal_code?: string
|
|
city?: string
|
|
country?: string
|
|
org_number?: string
|
|
vat_number?: string
|
|
default_payment_terms?: number
|
|
notes?: string
|
|
}
|
|
|
|
export interface CreateInvoiceInput {
|
|
customer_id: string
|
|
invoice_date: string
|
|
due_date: string
|
|
currency: Currency
|
|
your_reference?: string
|
|
our_reference?: string
|
|
notes?: string
|
|
items: CreateInvoiceItemInput[]
|
|
}
|
|
|
|
export interface CreateInvoiceItemInput {
|
|
description: string
|
|
quantity: number
|
|
unit: string
|
|
unit_price: number
|
|
}
|
|
|
|
export interface CreateTransactionInput {
|
|
date: string
|
|
description: string
|
|
amount: number
|
|
currency: Currency
|
|
category?: TransactionCategory
|
|
is_business?: boolean
|
|
notes?: string
|
|
}
|
|
|
|
// API Response types
|
|
export interface ApiResponse<T> {
|
|
data?: T
|
|
error?: string
|
|
}
|
|
|
|
export interface PaginatedResponse<T> {
|
|
data: T[]
|
|
count: number
|
|
page: number
|
|
pageSize: number
|
|
totalPages: number
|
|
}
|
|
|
|
// VAT validation response
|
|
export interface VatValidationResult {
|
|
valid: boolean
|
|
name?: string
|
|
address?: string
|
|
country_code?: string
|
|
vat_number?: string
|
|
error?: string
|
|
}
|
|
|
|
// Exchange rate response
|
|
export interface ExchangeRate {
|
|
currency: Currency
|
|
rate: number
|
|
date: string
|
|
}
|
|
|
|
// Dashboard summary types
|
|
export interface DashboardSummary {
|
|
// Income
|
|
total_income_ytd: number
|
|
total_income_mtd: number
|
|
|
|
// Expenses
|
|
total_expenses_ytd: number
|
|
total_expenses_mtd: number
|
|
|
|
// Net
|
|
net_income_ytd: number
|
|
net_income_mtd: number
|
|
|
|
// Tax estimates
|
|
estimated_tax: TaxEstimate
|
|
|
|
// Alerts
|
|
uncategorized_count: number
|
|
unpaid_invoices_count: number
|
|
unpaid_invoices_total: number
|
|
overdue_invoices_count: number
|
|
|
|
// Bank
|
|
bank_balance: number | null
|
|
available_balance: number | null // After tax reservations
|
|
}
|
|
|
|
export interface TaxEstimate {
|
|
// For EF
|
|
egenavgifter?: number
|
|
income_tax?: number // Municipal tax (kommunalskatt)
|
|
state_tax?: number // State tax (statlig skatt) - 20% on high incomes
|
|
grundavdrag?: number // Basic deduction applied
|
|
|
|
// For AB
|
|
bolagsskatt?: number
|
|
|
|
// Common
|
|
moms_to_pay: number
|
|
total_tax_liability: number
|
|
|
|
// Comparison with preliminary
|
|
preliminary_paid_ytd: number
|
|
difference: number // Positive = underpaying
|
|
|
|
// Schablonavdrag (if applied)
|
|
schablonavdrag_deduction?: number
|
|
}
|
|
|
|
// ============================================================
|
|
// BAS Kontoplan & Bookkeeping Types
|
|
// ============================================================
|
|
|
|
// Risk levels for mapping rules
|
|
export type RiskLevel = 'NONE' | 'LOW' | 'MEDIUM' | 'HIGH' | 'VERY_HIGH'
|
|
|
|
// Account types
|
|
export type AccountType = 'asset' | 'equity' | 'liability' | 'revenue' | 'expense'
|
|
export type NormalBalance = 'debit' | 'credit'
|
|
export type PlanType = 'k1' | 'full_bas'
|
|
|
|
// Journal entry source
|
|
export type JournalEntrySourceType =
|
|
| 'manual'
|
|
| 'bank_transaction'
|
|
| 'invoice_created'
|
|
| 'invoice_paid'
|
|
| 'credit_note'
|
|
| 'salary_payment'
|
|
| 'opening_balance'
|
|
| 'year_end'
|
|
| 'gift'
|
|
|
|
// Journal entry status
|
|
export type JournalEntryStatus = 'draft' | 'posted' | 'reversed'
|
|
|
|
// Mapping rule type
|
|
export type MappingRuleType =
|
|
| 'mcc_code'
|
|
| 'merchant_name'
|
|
| 'description_pattern'
|
|
| 'amount_threshold'
|
|
| 'combined'
|
|
|
|
// BAS Account
|
|
export interface BASAccount {
|
|
id: string
|
|
user_id: string
|
|
account_number: string
|
|
account_name: string
|
|
account_class: number
|
|
account_group: string
|
|
account_type: AccountType
|
|
normal_balance: NormalBalance
|
|
plan_type: PlanType
|
|
is_active: boolean
|
|
is_system_account: boolean
|
|
default_vat_code: string | null
|
|
description: string | null
|
|
sort_order: number
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Fiscal Period (Räkenskapsår)
|
|
export interface FiscalPeriod {
|
|
id: string
|
|
user_id: string
|
|
name: string
|
|
period_start: string
|
|
period_end: string
|
|
is_closed: boolean
|
|
closed_at: string | null
|
|
opening_balances_set: boolean
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Journal Entry (Verifikation)
|
|
export interface JournalEntry {
|
|
id: string
|
|
user_id: string
|
|
fiscal_period_id: string
|
|
voucher_number: number
|
|
voucher_series: string
|
|
entry_date: string
|
|
description: string
|
|
source_type: JournalEntrySourceType
|
|
source_id: string | null
|
|
status: JournalEntryStatus
|
|
attachment_urls: string[] | null
|
|
created_at: string
|
|
updated_at: string
|
|
// Relations
|
|
lines?: JournalEntryLine[]
|
|
}
|
|
|
|
// Journal Entry Line
|
|
export interface JournalEntryLine {
|
|
id: string
|
|
journal_entry_id: string
|
|
account_number: string
|
|
account_id: string | null
|
|
debit_amount: number
|
|
credit_amount: number
|
|
currency: string
|
|
amount_in_currency: number | null
|
|
exchange_rate: number | null
|
|
line_description: string | null
|
|
sort_order: number
|
|
created_at: string
|
|
}
|
|
|
|
// Mapping Rule
|
|
export interface MappingRule {
|
|
id: string
|
|
user_id: string | null
|
|
rule_name: string
|
|
rule_type: MappingRuleType
|
|
priority: number
|
|
// Matching
|
|
mcc_codes: number[] | null
|
|
merchant_pattern: string | null
|
|
description_pattern: string | null
|
|
amount_min: number | null
|
|
amount_max: number | null
|
|
// Targets
|
|
debit_account: string | null
|
|
credit_account: string | null
|
|
vat_treatment: string | null
|
|
vat_debit_account: string | null
|
|
vat_credit_account: string | null
|
|
// Risk
|
|
risk_level: RiskLevel
|
|
default_private: boolean
|
|
requires_review: boolean
|
|
confidence_score: number
|
|
// Capitalization
|
|
capitalization_threshold: number | null
|
|
capitalized_debit_account: string | null
|
|
// Meta
|
|
is_active: boolean
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Mapping engine result
|
|
export interface MappingResult {
|
|
rule: MappingRule | null
|
|
debit_account: string
|
|
credit_account: string
|
|
risk_level: RiskLevel
|
|
confidence: number
|
|
requires_review: boolean
|
|
default_private: boolean
|
|
vat_lines: VatJournalLine[]
|
|
description: string
|
|
}
|
|
|
|
// VAT journal line (auto-generated)
|
|
export interface VatJournalLine {
|
|
account_number: string
|
|
debit_amount: number
|
|
credit_amount: number
|
|
description: string
|
|
}
|
|
|
|
// Account Balance (cached)
|
|
export interface AccountBalance {
|
|
id: string
|
|
user_id: string
|
|
fiscal_period_id: string
|
|
account_number: string
|
|
account_id: string | null
|
|
opening_debit: number
|
|
opening_credit: number
|
|
period_debit: number
|
|
period_credit: number
|
|
closing_debit: number
|
|
closing_credit: number
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Report types
|
|
export interface TrialBalanceRow {
|
|
account_number: string
|
|
account_name: string
|
|
account_class: number
|
|
opening_debit: number
|
|
opening_credit: number
|
|
period_debit: number
|
|
period_credit: number
|
|
closing_debit: number
|
|
closing_credit: number
|
|
}
|
|
|
|
export interface IncomeStatementSection {
|
|
title: string
|
|
rows: { account_number: string; account_name: string; amount: number }[]
|
|
subtotal: number
|
|
}
|
|
|
|
export interface IncomeStatementReport {
|
|
revenue_sections: IncomeStatementSection[]
|
|
total_revenue: number
|
|
expense_sections: IncomeStatementSection[]
|
|
total_expenses: number
|
|
financial_sections: IncomeStatementSection[]
|
|
total_financial: number
|
|
net_result: number
|
|
period: { start: string; end: string }
|
|
}
|
|
|
|
export interface BalanceSheetSection {
|
|
title: string
|
|
rows: { account_number: string; account_name: string; amount: number }[]
|
|
subtotal: number
|
|
}
|
|
|
|
export interface BalanceSheetReport {
|
|
asset_sections: BalanceSheetSection[]
|
|
total_assets: number
|
|
equity_liability_sections: BalanceSheetSection[]
|
|
total_equity_liabilities: number
|
|
period: { start: string; end: string }
|
|
}
|
|
|
|
export interface SIEExportOptions {
|
|
fiscal_period_id: string
|
|
company_name: string
|
|
org_number: string | null
|
|
program_name?: string
|
|
}
|
|
|
|
// Input types for creating entries
|
|
export interface CreateJournalEntryInput {
|
|
fiscal_period_id: string
|
|
entry_date: string
|
|
description: string
|
|
source_type: JournalEntrySourceType
|
|
source_id?: string
|
|
voucher_series?: string
|
|
lines: CreateJournalEntryLineInput[]
|
|
}
|
|
|
|
export interface CreateJournalEntryLineInput {
|
|
account_number: string
|
|
debit_amount: number
|
|
credit_amount: number
|
|
line_description?: string
|
|
currency?: string
|
|
amount_in_currency?: number
|
|
exchange_rate?: number
|
|
}
|
|
|
|
export interface CreateFiscalPeriodInput {
|
|
name: string
|
|
period_start: string
|
|
period_end: string
|
|
}
|
|
|
|
// ============================================================
|
|
// Schablonavdrag (Standard Deductions) Types
|
|
// ============================================================
|
|
|
|
// Housing type for hemmakontor deduction
|
|
export type HousingType = 'villa' | 'apartment' // villa = 2000 kr/year, apartment = 4000 kr/year
|
|
|
|
// Schablonavdrag settings stored in company_settings
|
|
export interface SchablonavdragSettings {
|
|
hemmakontor_enabled: boolean
|
|
hemmakontor_housing_type?: HousingType // 'villa' or 'apartment' (default: apartment)
|
|
bil_enabled: boolean
|
|
}
|
|
|
|
// Mileage entry for bil schablonavdrag
|
|
export interface MileageEntry {
|
|
id: string
|
|
user_id: string
|
|
date: string
|
|
distance_km: number
|
|
purpose: string
|
|
from_location: string | null
|
|
to_location: string | null
|
|
rate_per_km: number
|
|
total_deduction: number
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Input for creating mileage entries
|
|
export interface CreateMileageEntryInput {
|
|
date: string
|
|
distance_km: number
|
|
purpose: string
|
|
from_location?: string
|
|
to_location?: string
|
|
}
|
|
|
|
// Schablonavdrag summary for a year
|
|
export interface SchablonavdragSummary {
|
|
year: number
|
|
hemmakontor: {
|
|
enabled: boolean
|
|
housing_type: HousingType
|
|
months_active: number
|
|
annual_amount: number // 2000 for villa, 4000 for apartment
|
|
deduction: number
|
|
}
|
|
mileage: {
|
|
enabled: boolean
|
|
total_km: number
|
|
rate_per_km: number // 2.50 kr/km (25 kr/mil)
|
|
total_deduction: number
|
|
entries_count: number
|
|
}
|
|
total_deduction: number
|
|
}
|
|
|
|
// Tax warning levels
|
|
export type TaxWarningLevel = 'safe' | 'info' | 'warning' | 'danger'
|
|
|
|
// Onboarding progress for new user checklist
|
|
export interface OnboardingProgress {
|
|
hasCustomers: boolean
|
|
hasInvoices: boolean
|
|
hasReceipts: boolean
|
|
hasBankConnected: boolean
|
|
}
|
|
|
|
// Enhanced tax warning status
|
|
export interface TaxWarningStatus {
|
|
level: TaxWarningLevel
|
|
message: string
|
|
percentageDifference: number
|
|
yearEndProjection?: {
|
|
estimatedTotalTax: number
|
|
projectedPreliminaryPayments: number
|
|
projectedDifference: number
|
|
}
|
|
recommendation?: string
|
|
}
|
|
|
|
// Onboarding step data
|
|
export interface OnboardingStepData {
|
|
step1?: {
|
|
entity_type: EntityType
|
|
}
|
|
step2?: {
|
|
company_name: string
|
|
org_number?: string
|
|
address_line1?: string
|
|
postal_code?: string
|
|
city?: string
|
|
}
|
|
step3?: {
|
|
f_skatt: boolean
|
|
fiscal_year_start_month: number
|
|
vat_registered: boolean
|
|
vat_number?: string
|
|
moms_period?: MomsPeriod
|
|
}
|
|
step4?: {
|
|
preliminary_tax_monthly?: number
|
|
}
|
|
step5?: {
|
|
bank_name?: string
|
|
clearing_number?: string
|
|
account_number?: string
|
|
iban?: string
|
|
bic?: string
|
|
}
|
|
step6?: {
|
|
bank_connected: boolean
|
|
bank_connection_id?: string
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// Gift/Benefit Types (Förmånshantering)
|
|
// ============================================================
|
|
|
|
// Booking type determines how the gift affects bookkeeping
|
|
export type GiftBookingType = 'income' | 'income_and_expense' | 'tax_free'
|
|
|
|
// NE-bilaga ruta for income
|
|
export type NEIncomeRuta = 'R1' | 'R2'
|
|
|
|
// NE-bilaga ruta for expenses
|
|
export type NEExpenseRuta = 'R6'
|
|
|
|
// Classification result from the decision tree
|
|
export interface GiftClassification {
|
|
taxable: boolean
|
|
marketValue: number
|
|
deductibleAsExpense: boolean
|
|
bookingType: GiftBookingType
|
|
reasoning: string
|
|
// VAT handling (moms)
|
|
vatLiable: boolean // Ska moms redovisas? (bytestransaktion vid motprestation)
|
|
vatAmount: number // Momsbelopp (25% av värde exkl moms)
|
|
valueExclVat: number // Värde exkl moms (för bokföring)
|
|
// NE-bilaga mapping
|
|
neIncomeRuta: NEIncomeRuta | null // R1 (momspliktig) eller R2 (momsfri)
|
|
neExpenseRuta: NEExpenseRuta | null // R6 om avdragsgill
|
|
}
|
|
|
|
// Input for classifying a gift
|
|
export interface GiftInput {
|
|
estimatedValue: number
|
|
hasMotprestation: boolean // Required post/video/mention?
|
|
usedInBusiness: boolean // Used as props/equipment?
|
|
usedPrivately: boolean // Personal use?
|
|
isSimplePromoItem: boolean // Pen, mug, basic merch?
|
|
}
|
|
|
|
// Gift record stored in database
|
|
export interface Gift {
|
|
id: string
|
|
user_id: string
|
|
date: string
|
|
brand_name: string
|
|
description: string
|
|
estimated_value: number
|
|
|
|
// Decision tree inputs
|
|
has_motprestation: boolean
|
|
used_in_business: boolean
|
|
used_privately: boolean
|
|
is_simple_promo: boolean
|
|
|
|
// Classification result
|
|
classification: GiftClassification
|
|
|
|
// Bookkeeping link
|
|
journal_entry_id: string | null
|
|
|
|
// Light mode / extended fields
|
|
returned: boolean
|
|
campaign_id: string | null
|
|
image_url: string | null
|
|
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Input for creating a gift
|
|
export interface CreateGiftInput {
|
|
date: string
|
|
brand_name: string
|
|
description: string
|
|
estimated_value: number
|
|
has_motprestation: boolean
|
|
used_in_business: boolean
|
|
used_privately: boolean
|
|
is_simple_promo?: boolean
|
|
}
|
|
|
|
// Gift summary for a year (used in dashboard and reports)
|
|
export interface GiftSummary {
|
|
year: number
|
|
total_count: number
|
|
total_value: number
|
|
taxable_count: number
|
|
taxable_value: number
|
|
tax_free_count: number
|
|
tax_free_value: number
|
|
deductible_count: number
|
|
deductible_value: number
|
|
}
|
|
|
|
// ============================================================
|
|
// Shadow Ledger & Light Mode Types
|
|
// ============================================================
|
|
|
|
export interface ShadowLedgerEntry {
|
|
id: string
|
|
user_id: string
|
|
date: string
|
|
type: ShadowLedgerEntryType
|
|
source: ShadowLedgerSource
|
|
provider: string | null
|
|
|
|
// Gross-to-net breakdown
|
|
gross_amount: number
|
|
platform_fee: number
|
|
service_fee: number
|
|
pension_deduction: number
|
|
social_fees: number
|
|
income_tax_withheld: number
|
|
net_amount: number
|
|
|
|
// Currency
|
|
currency: string
|
|
amount_sek: number | null
|
|
exchange_rate: number | null
|
|
|
|
// Linking
|
|
description: string | null
|
|
bank_transaction_id: string | null
|
|
gift_id: string | null
|
|
campaign_id: string | null
|
|
metadata: Record<string, unknown>
|
|
|
|
// Virtual tax debt
|
|
virtual_tax_debt: number
|
|
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface CreateShadowLedgerEntryInput {
|
|
date: string
|
|
type: ShadowLedgerEntryType
|
|
source?: ShadowLedgerSource
|
|
provider?: string
|
|
gross_amount: number
|
|
platform_fee?: number
|
|
service_fee?: number
|
|
pension_deduction?: number
|
|
social_fees?: number
|
|
income_tax_withheld?: number
|
|
net_amount: number
|
|
currency?: string
|
|
description?: string
|
|
bank_transaction_id?: string
|
|
gift_id?: string
|
|
campaign_id?: string
|
|
metadata?: Record<string, unknown>
|
|
}
|
|
|
|
export interface ShadowLedgerSummary {
|
|
year: number
|
|
total_gross: number
|
|
total_net: number
|
|
total_fees: number
|
|
total_tax_withheld: number
|
|
total_pension: number
|
|
total_social_fees: number
|
|
total_platform_fees: number
|
|
virtual_tax_debt: number
|
|
entry_count: number
|
|
by_type: Record<ShadowLedgerEntryType, { count: number; gross: number; net: number }>
|
|
}
|
|
|
|
export interface LightTaxEstimate {
|
|
taxable_gift_value: number
|
|
municipal_tax_rate: number
|
|
church_tax_rate: number
|
|
gift_tax: number
|
|
hobby_tax: number
|
|
total_virtual_debt: number
|
|
safe_to_spend: number
|
|
safe_to_spend_percent: number
|
|
safe_to_spend_level: 'green' | 'yellow' | 'red'
|
|
}
|
|
|
|
export interface MunicipalityRate {
|
|
id: number
|
|
municipality_code: string
|
|
municipality_name: string
|
|
parish_code: string | null
|
|
parish_name: string | null
|
|
municipality_rate: number | null
|
|
county_rate: number | null
|
|
burial_fee: number | null
|
|
total_rate: number
|
|
church_rate: number | null
|
|
year: number
|
|
}
|
|
|
|
export interface UmbrellaProviderDefault {
|
|
id: string
|
|
display_name: string
|
|
default_fee_percent: number | null
|
|
pension_percent: number | null
|
|
pension_age_min: number | null
|
|
pension_age_max: number | null
|
|
notes: string | null
|
|
}
|
|
|
|
// ============================================================
|
|
// Campaign & Contract Types
|
|
// ============================================================
|
|
|
|
// Campaign status
|
|
export type CampaignStatus =
|
|
| 'negotiation' // Under förhandling
|
|
| 'contracted' // Avtal signerat, ej startat
|
|
| 'active' // Pågående
|
|
| 'delivered' // Alla leverabler klara
|
|
| 'invoiced' // Faktura skickad
|
|
| 'completed' // Avslutat och betalt
|
|
| 'cancelled' // Avbrutet
|
|
|
|
// Campaign type
|
|
export type CampaignType =
|
|
| 'influencer' // Standard influencer-kampanj
|
|
| 'ugc' // Endast UGC-material
|
|
| 'ambassador' // Långsiktigt ambassadörskap
|
|
|
|
// Deliverable type
|
|
export type DeliverableType =
|
|
| 'video' // Video (YouTube, TikTok-video)
|
|
| 'image' // Stillbild/foto
|
|
| 'story' // Instagram/TikTok story
|
|
| 'reel' // Instagram/TikTok reel
|
|
| 'post' // Inlägg i flöde
|
|
| 'raw_material' // Råmaterial för varumärkets användning
|
|
|
|
// Deliverable status
|
|
export type DeliverableStatus =
|
|
| 'pending' // Inte påbörjat
|
|
| 'in_progress' // Under arbete
|
|
| 'submitted' // Inskickat för granskning
|
|
| 'revision' // Behöver ändringar
|
|
| 'approved' // Godkänt av varumärke
|
|
| 'published' // Publicerat/live
|
|
|
|
// Platform type
|
|
export type PlatformType =
|
|
| 'instagram'
|
|
| 'tiktok'
|
|
| 'youtube'
|
|
| 'blog'
|
|
| 'podcast'
|
|
| 'other'
|
|
|
|
// Customer category (for campaigns)
|
|
export type CustomerCategory = 'brand' | 'agency' | 'platform'
|
|
|
|
// Payment status (for invoices)
|
|
export type PaymentStatus = 'pending' | 'paid' | 'overdue' | 'partial'
|
|
|
|
// Date calculation type (for relative dates)
|
|
export type DateCalculationType = 'absolute' | 'relative'
|
|
|
|
// Reference event for relative dates
|
|
export type ReferenceEvent = 'publication' | 'delivery' | 'approval' | 'contract'
|
|
|
|
// Billing frequency
|
|
export type BillingFrequency = 'upfront' | 'on_delivery' | 'monthly' | 'split'
|
|
|
|
// Contract extraction status
|
|
export type ExtractionStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'skipped'
|
|
|
|
// Campaign record
|
|
export interface Campaign {
|
|
id: string
|
|
user_id: string
|
|
customer_id: string | null
|
|
end_customer_id: string | null
|
|
|
|
name: string
|
|
description: string | null
|
|
status: CampaignStatus
|
|
campaign_type: CampaignType
|
|
|
|
total_value: number | null
|
|
currency: string
|
|
vat_included: boolean
|
|
payment_terms: number | null
|
|
billing_frequency: BillingFrequency | null
|
|
|
|
start_date: string | null
|
|
end_date: string | null
|
|
publication_date: string | null
|
|
draft_deadline: string | null
|
|
brand_name: string | null
|
|
contract_signed_at: string | null
|
|
|
|
notes: string | null
|
|
created_at: string
|
|
updated_at: string
|
|
|
|
// Relations (populated when fetched)
|
|
customer?: Customer
|
|
end_customer?: Customer
|
|
deliverables?: Deliverable[]
|
|
exclusivities?: Exclusivity[]
|
|
contracts?: Contract[]
|
|
invoices?: Invoice[]
|
|
briefings?: Briefing[]
|
|
}
|
|
|
|
// Deliverable record
|
|
export interface Deliverable {
|
|
id: string
|
|
campaign_id: string
|
|
user_id: string
|
|
|
|
title: string
|
|
deliverable_type: DeliverableType
|
|
platform: PlatformType
|
|
account_handle: string | null
|
|
|
|
quantity: number
|
|
description: string | null
|
|
specifications: Record<string, unknown>
|
|
|
|
due_date: string | null
|
|
status: DeliverableStatus
|
|
submitted_at: string | null
|
|
approved_at: string | null
|
|
published_at: string | null
|
|
|
|
notes: string | null
|
|
created_at: string
|
|
updated_at: string
|
|
|
|
// Relations
|
|
campaign?: Campaign
|
|
}
|
|
|
|
// Exclusivity record
|
|
export interface Exclusivity {
|
|
id: string
|
|
campaign_id: string
|
|
user_id: string
|
|
|
|
categories: string[]
|
|
excluded_brands: string[]
|
|
|
|
start_date: string
|
|
end_date: string
|
|
|
|
start_calculation_type: DateCalculationType
|
|
end_calculation_type: DateCalculationType
|
|
start_reference: ReferenceEvent | null
|
|
end_reference: ReferenceEvent | null
|
|
start_offset_days: number | null
|
|
end_offset_days: number | null
|
|
|
|
notes: string | null
|
|
created_at: string
|
|
updated_at: string
|
|
|
|
// Relations
|
|
campaign?: Campaign
|
|
}
|
|
|
|
// Contract record
|
|
export interface Contract {
|
|
id: string
|
|
campaign_id: string
|
|
user_id: string
|
|
|
|
filename: string
|
|
file_path: string
|
|
file_size: number | null
|
|
mime_type: string | null
|
|
|
|
extracted_data: Record<string, unknown>
|
|
extraction_status: ExtractionStatus
|
|
|
|
signing_date: string | null
|
|
is_primary: boolean
|
|
|
|
notes: string | null
|
|
uploaded_at: string
|
|
created_at: string
|
|
updated_at: string
|
|
|
|
// Relations
|
|
campaign?: Campaign
|
|
}
|
|
|
|
// Input types for creating campaigns
|
|
export interface CreateCampaignInput {
|
|
customer_id?: string
|
|
end_customer_id?: string
|
|
name: string
|
|
description?: string
|
|
campaign_type?: CampaignType
|
|
total_value?: number
|
|
currency?: string
|
|
vat_included?: boolean
|
|
payment_terms?: number
|
|
billing_frequency?: BillingFrequency
|
|
start_date?: string
|
|
end_date?: string
|
|
publication_date?: string
|
|
draft_deadline?: string
|
|
brand_name?: string
|
|
notes?: string
|
|
}
|
|
|
|
// Input types for creating deliverables
|
|
export interface CreateDeliverableInput {
|
|
campaign_id: string
|
|
title: string
|
|
deliverable_type: DeliverableType
|
|
platform: PlatformType
|
|
account_handle?: string
|
|
quantity?: number
|
|
description?: string
|
|
specifications?: Record<string, unknown>
|
|
due_date?: string
|
|
notes?: string
|
|
}
|
|
|
|
// Input types for creating exclusivities
|
|
export interface CreateExclusivityInput {
|
|
campaign_id: string
|
|
categories: string[]
|
|
excluded_brands?: string[]
|
|
start_date: string
|
|
end_date: string
|
|
start_calculation_type?: DateCalculationType
|
|
end_calculation_type?: DateCalculationType
|
|
start_reference?: ReferenceEvent
|
|
end_reference?: ReferenceEvent
|
|
start_offset_days?: number
|
|
end_offset_days?: number
|
|
notes?: string
|
|
}
|
|
|
|
// Input types for uploading contracts
|
|
export interface CreateContractInput {
|
|
campaign_id: string
|
|
filename: string
|
|
file_path: string
|
|
file_size?: number
|
|
mime_type?: string
|
|
signing_date?: string
|
|
is_primary?: boolean
|
|
notes?: string
|
|
}
|
|
|
|
// Exclusivity conflict detection
|
|
export interface ExclusivityConflict {
|
|
existingExclusivity: Exclusivity
|
|
conflictingCampaign: Campaign
|
|
overlappingCategories: string[]
|
|
overlapStart: string
|
|
overlapEnd: string
|
|
}
|
|
|
|
// Workload analysis
|
|
export interface WorkloadAnalysis {
|
|
date: string
|
|
deliverables: Deliverable[]
|
|
totalDeliverables: number
|
|
byPlatform: Record<PlatformType, number>
|
|
byType: Record<DeliverableType, number>
|
|
workloadLevel: 'light' | 'normal' | 'heavy' | 'overloaded'
|
|
}
|
|
|
|
// Campaign summary for dashboard
|
|
export interface CampaignSummary {
|
|
totalCampaigns: number
|
|
activeCampaigns: number
|
|
totalValue: number
|
|
byStatus: Record<CampaignStatus, number>
|
|
upcomingDeliverables: Deliverable[]
|
|
activeExclusivities: Exclusivity[]
|
|
}
|
|
|
|
// Swedish labels for enums
|
|
export const CAMPAIGN_STATUS_LABELS: Record<CampaignStatus, string> = {
|
|
negotiation: 'Förhandling',
|
|
contracted: 'Avtalat',
|
|
active: 'Pågående',
|
|
delivered: 'Levererat',
|
|
invoiced: 'Fakturerat',
|
|
completed: 'Avslutat',
|
|
cancelled: 'Avbrutet'
|
|
}
|
|
|
|
export const CAMPAIGN_TYPE_LABELS: Record<CampaignType, string> = {
|
|
influencer: 'Influencer-samarbete',
|
|
ugc: 'UGC',
|
|
ambassador: 'Ambassadör'
|
|
}
|
|
|
|
export const DELIVERABLE_TYPE_LABELS: Record<DeliverableType, string> = {
|
|
video: 'Video',
|
|
image: 'Bild',
|
|
story: 'Story',
|
|
reel: 'Reel',
|
|
post: 'Inlägg',
|
|
raw_material: 'Råmaterial'
|
|
}
|
|
|
|
export const DELIVERABLE_STATUS_LABELS: Record<DeliverableStatus, string> = {
|
|
pending: 'Väntar',
|
|
in_progress: 'Pågår',
|
|
submitted: 'Inskickat',
|
|
revision: 'Revision',
|
|
approved: 'Godkänt',
|
|
published: 'Publicerat'
|
|
}
|
|
|
|
export const PLATFORM_LABELS: Record<PlatformType, string> = {
|
|
instagram: 'Instagram',
|
|
tiktok: 'TikTok',
|
|
youtube: 'YouTube',
|
|
blog: 'Blogg',
|
|
podcast: 'Podcast',
|
|
other: 'Övrigt'
|
|
}
|
|
|
|
export const CUSTOMER_CATEGORY_LABELS: Record<CustomerCategory, string> = {
|
|
brand: 'Varumärke',
|
|
agency: 'Byrå',
|
|
platform: 'Plattform'
|
|
}
|
|
|
|
export const PAYMENT_STATUS_LABELS: Record<PaymentStatus, string> = {
|
|
pending: 'Väntar',
|
|
paid: 'Betald',
|
|
overdue: 'Försenad',
|
|
partial: 'Delbetalad'
|
|
}
|
|
|
|
export const BILLING_FREQUENCY_LABELS: Record<BillingFrequency, string> = {
|
|
upfront: 'I förskott',
|
|
on_delivery: 'Vid leverans',
|
|
monthly: 'Månadsvis',
|
|
split: 'Delbetalning'
|
|
}
|
|
|
|
// ============================================================
|
|
// Calendar & Deadline Types
|
|
// ============================================================
|
|
|
|
// Calendar view mode
|
|
export type CalendarViewMode = 'month' | 'week' | 'day'
|
|
|
|
// Payment calendar day (for invoice due date tracking)
|
|
export interface PaymentCalendarDay {
|
|
date: string
|
|
invoices: Invoice[]
|
|
totalExpected: number
|
|
overdueCount: number
|
|
}
|
|
|
|
// Tax deadline types (Swedish Skatteverket)
|
|
export type TaxDeadlineType =
|
|
| 'moms_monthly'
|
|
| 'moms_quarterly'
|
|
| 'moms_yearly'
|
|
| 'f_skatt'
|
|
| 'arbetsgivardeklaration'
|
|
| 'inkomstdeklaration_ef'
|
|
| 'inkomstdeklaration_ab'
|
|
| 'arsredovisning'
|
|
| 'periodisk_sammanstallning'
|
|
| 'bokslut'
|
|
|
|
// Deadline status workflow
|
|
export type DeadlineStatus =
|
|
| 'upcoming' // More than 14 days away
|
|
| 'action_needed' // Within 14 days, needs attention
|
|
| 'in_progress' // User is working on it
|
|
| 'submitted' // Submitted to Skatteverket
|
|
| 'confirmed' // Confirmed/acknowledged
|
|
| 'overdue' // Past due date without submission
|
|
|
|
// Deadline source
|
|
export type DeadlineSource = 'system' | 'user'
|
|
|
|
// Deadline types (extended for campaigns and tax)
|
|
export type DeadlineType = 'delivery' | 'approval' | 'invoicing' | 'report' | 'revision' | 'assets' | 'spark_ad' | 'statistics' | 'tax' | 'other'
|
|
export type DeadlinePriority = 'critical' | 'important' | 'normal'
|
|
|
|
// Deadline record (extended for campaigns and tax)
|
|
export interface Deadline {
|
|
id: string
|
|
user_id: string
|
|
title: string
|
|
due_date: string
|
|
due_time: string | null
|
|
deadline_type: DeadlineType
|
|
priority: DeadlinePriority
|
|
is_completed: boolean
|
|
completed_at: string | null
|
|
customer_id: string | null
|
|
campaign_id: string | null
|
|
deliverable_id: string | null
|
|
is_auto_generated: boolean
|
|
date_calculation_type: DateCalculationType | null
|
|
reference_event: ReferenceEvent | null
|
|
offset_days: number | null
|
|
notes: string | null
|
|
created_at: string
|
|
updated_at: string
|
|
|
|
// Tax deadline fields
|
|
tax_deadline_type: TaxDeadlineType | null
|
|
tax_period: string | null
|
|
source: DeadlineSource
|
|
reminder_offsets: number[] | null
|
|
status: DeadlineStatus
|
|
status_changed_at: string
|
|
linked_report_type: string | null
|
|
linked_report_period: Record<string, unknown> | null
|
|
|
|
// Relations
|
|
customer?: Customer
|
|
campaign?: Campaign
|
|
deliverable?: Deliverable
|
|
}
|
|
|
|
// Input for creating a deadline
|
|
export interface CreateDeadlineInput {
|
|
title: string
|
|
due_date: string
|
|
due_time?: string
|
|
deadline_type: DeadlineType
|
|
priority?: DeadlinePriority
|
|
customer_id?: string
|
|
notes?: string
|
|
// Tax deadline fields
|
|
tax_deadline_type?: TaxDeadlineType
|
|
tax_period?: string
|
|
source?: DeadlineSource
|
|
linked_report_type?: string
|
|
linked_report_period?: Record<string, unknown>
|
|
}
|
|
|
|
// ============================================================
|
|
// Push Notification 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
|
|
campaign_deadlines_enabled: boolean
|
|
quiet_start: string // time format "HH:MM"
|
|
quiet_end: string // time format "HH:MM"
|
|
email_enabled: boolean
|
|
push_enabled: boolean
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Notification type for logging
|
|
export type NotificationType = 'tax_deadline' | 'invoice_due' | 'invoice_overdue' | 'campaign_deadline'
|
|
|
|
// 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)
|
|
// ============================================================
|
|
|
|
// Calendar feed for Apple Calendar / Google Calendar sync
|
|
export interface CalendarFeed {
|
|
id: string
|
|
user_id: string
|
|
feed_token: string
|
|
is_active: boolean
|
|
include_tax_deadlines: boolean
|
|
include_invoices: boolean
|
|
include_campaigns: boolean
|
|
include_exclusivity: boolean
|
|
last_accessed_at: string | null
|
|
access_count: number
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Input for creating/updating calendar feed
|
|
export interface UpdateCalendarFeedInput {
|
|
include_tax_deadlines?: boolean
|
|
include_invoices?: boolean
|
|
include_campaigns?: boolean
|
|
include_exclusivity?: boolean
|
|
}
|
|
|
|
// Swedish labels for deadline status
|
|
export const DEADLINE_STATUS_LABELS: Record<DeadlineStatus, string> = {
|
|
upcoming: 'Kommande',
|
|
action_needed: 'Åtgärd krävs',
|
|
in_progress: 'Pågår',
|
|
submitted: 'Inskickad',
|
|
confirmed: 'Bekräftad',
|
|
overdue: 'Försenad'
|
|
}
|
|
|
|
// Swedish labels for tax deadline types
|
|
export const TAX_DEADLINE_TYPE_LABELS: Record<TaxDeadlineType, string> = {
|
|
moms_monthly: 'Momsdeklaration (månad)',
|
|
moms_quarterly: 'Momsdeklaration (kvartal)',
|
|
moms_yearly: 'Momsdeklaration (år)',
|
|
f_skatt: 'F-skatt',
|
|
arbetsgivardeklaration: 'Arbetsgivardeklaration',
|
|
inkomstdeklaration_ef: 'Inkomstdeklaration EF',
|
|
inkomstdeklaration_ab: 'Inkomstdeklaration AB',
|
|
arsredovisning: 'Årsredovisning',
|
|
periodisk_sammanstallning: 'Periodisk sammanställning',
|
|
bokslut: 'Bokslut'
|
|
}
|
|
|
|
// ============================================================
|
|
// SIE Import Types
|
|
// ============================================================
|
|
|
|
// SIE import status
|
|
export type SIEImportStatus = 'pending' | 'mapped' | 'completed' | 'failed'
|
|
|
|
// SIE import record
|
|
export interface SIEImport {
|
|
id: string
|
|
user_id: string
|
|
filename: string
|
|
file_hash: string
|
|
org_number: string | null
|
|
company_name: string | null
|
|
sie_type: number
|
|
fiscal_year_start: string | null
|
|
fiscal_year_end: string | null
|
|
accounts_count: number
|
|
transactions_count: number
|
|
opening_balance_total: number | null
|
|
status: SIEImportStatus
|
|
error_message: string | null
|
|
fiscal_period_id: string | null
|
|
opening_balance_entry_id: string | null
|
|
imported_at: string | null
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// SIE account mapping record
|
|
export interface SIEAccountMapping {
|
|
id: string
|
|
user_id: string
|
|
source_account: string
|
|
source_name: string | null
|
|
target_account: string
|
|
confidence: number
|
|
match_type: 'exact' | 'name' | 'class' | 'manual'
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// ============================================================
|
|
// Contract Extraction Types (AI-driven)
|
|
// ============================================================
|
|
|
|
// Confidence level for extracted fields
|
|
export type ConfidenceLevel = 'high' | 'medium' | 'low' | 'missing'
|
|
|
|
// Extracted party (brand or agency) from contract
|
|
export interface ExtractedParty {
|
|
name: string
|
|
orgNumber: string | null
|
|
email: string | null
|
|
contactPerson: string | null
|
|
}
|
|
|
|
// Extracted deliverable from contract
|
|
export interface ExtractedDeliverable {
|
|
type: DeliverableType
|
|
quantity: number
|
|
platform: PlatformType | null
|
|
account: string | null
|
|
dueDate: string | null
|
|
description: string | null
|
|
}
|
|
|
|
// Extracted deadline from contract
|
|
export interface ExtractedDeadline {
|
|
description: string
|
|
type: DeadlineType
|
|
absoluteDate: string | null
|
|
isRelative: boolean
|
|
referenceEvent: ReferenceEvent | null
|
|
offsetDays: number | null
|
|
}
|
|
|
|
// Complete extraction result from AI analysis
|
|
export interface ContractExtractionResult {
|
|
parties: {
|
|
brand: ExtractedParty | null
|
|
agency: ExtractedParty | null
|
|
}
|
|
financials: {
|
|
amount: number | null
|
|
currency: string
|
|
vatIncluded: boolean | null
|
|
paymentTerms: number | null
|
|
billingFrequency: BillingFrequency | null
|
|
}
|
|
deliverables: ExtractedDeliverable[]
|
|
period: {
|
|
startDate: string | null
|
|
endDate: string | null
|
|
publicationDate: string | null
|
|
draftDeadline: string | null
|
|
}
|
|
exclusivity: {
|
|
categories: string[]
|
|
excludedBrands: string[]
|
|
prePeriodDays: number | null
|
|
postPeriodDays: number | null
|
|
postReference: ReferenceEvent | null
|
|
}
|
|
deadlines: ExtractedDeadline[]
|
|
rights: {
|
|
usageType: 'organic' | 'paid' | 'both' | null
|
|
usagePeriodMonths: number | null
|
|
ownership: 'influencer' | 'client' | null
|
|
}
|
|
campaignName: string | null
|
|
signingDate: string | null
|
|
confidence: Record<string, ConfidenceLevel>
|
|
}
|
|
|
|
// Customer matching result
|
|
export type CustomerMatchType = 'exact' | 'probable' | 'none'
|
|
|
|
export interface CustomerMatchResult {
|
|
matchType: CustomerMatchType
|
|
customer: Customer | null
|
|
confidence: number
|
|
matchedOn: ('org_number' | 'name' | 'email')[]
|
|
suggestedNewCustomer?: Partial<CreateCustomerInput>
|
|
}
|
|
|
|
// Contract import wizard state
|
|
export interface ContractImportState {
|
|
step: number
|
|
contractId: string | null
|
|
extractionResult: ContractExtractionResult | null
|
|
extractionStatus: ExtractionStatus
|
|
customerMatch: {
|
|
brand: CustomerMatchResult | null
|
|
agency: CustomerMatchResult | null
|
|
}
|
|
selectedCustomerId: string | null
|
|
selectedAgencyId: string | null
|
|
editedData: Partial<ContractExtractionResult>
|
|
exclusivityConflicts: ExclusivityConflict[]
|
|
}
|
|
|
|
// Labels for confidence levels
|
|
export const CONFIDENCE_LABELS: Record<ConfidenceLevel, string> = {
|
|
high: 'Hög säkerhet',
|
|
medium: 'Medel säkerhet',
|
|
low: 'Låg säkerhet',
|
|
missing: 'Saknas'
|
|
}
|
|
|
|
// Labels for extraction status
|
|
export const EXTRACTION_STATUS_LABELS: Record<ExtractionStatus, string> = {
|
|
pending: 'Väntar',
|
|
processing: 'Analyserar',
|
|
completed: 'Klar',
|
|
failed: 'Misslyckades',
|
|
skipped: 'Hoppade över'
|
|
}
|
|
|
|
// ============================================================
|
|
// TikTok Integration Types
|
|
// ============================================================
|
|
|
|
// TikTok account status
|
|
export type TikTokAccountStatus = 'active' | 'expired' | 'revoked' | 'error'
|
|
|
|
// TikTok sync type
|
|
export type TikTokSyncType = 'stats' | 'videos' | 'full' | 'token_refresh'
|
|
|
|
// TikTok sync status
|
|
export type TikTokSyncStatus = 'started' | 'success' | 'partial' | 'failed'
|
|
|
|
// TikTok account record
|
|
export interface TikTokAccount {
|
|
id: string
|
|
user_id: string
|
|
|
|
// TikTok user info
|
|
tiktok_user_id: string
|
|
username: string
|
|
display_name: string | null
|
|
avatar_url: string | null
|
|
|
|
// Token expiration (encrypted tokens not exposed to frontend)
|
|
token_expires_at: string
|
|
refresh_token_expires_at: string
|
|
|
|
// Account status
|
|
status: TikTokAccountStatus
|
|
|
|
// Sync tracking
|
|
last_synced_at: string | null
|
|
last_stats_sync_at: string | null
|
|
last_video_sync_at: string | null
|
|
|
|
// Error tracking
|
|
last_error: string | null
|
|
error_count: number
|
|
|
|
// Timestamps
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// TikTok daily stats record
|
|
export interface TikTokDailyStats {
|
|
id: string
|
|
tiktok_account_id: string
|
|
user_id: string
|
|
|
|
// Date of snapshot
|
|
stats_date: string
|
|
|
|
// Follower metrics
|
|
follower_count: number
|
|
following_count: number
|
|
|
|
// Content metrics
|
|
likes_count: number
|
|
video_count: number
|
|
|
|
// Calculated change
|
|
follower_change: number | null
|
|
|
|
// Timestamps
|
|
created_at: string
|
|
}
|
|
|
|
// TikTok video record
|
|
export interface TikTokVideo {
|
|
id: string
|
|
tiktok_account_id: string
|
|
user_id: string
|
|
|
|
// TikTok video identification
|
|
tiktok_video_id: string
|
|
|
|
// Video metadata
|
|
title: string | null
|
|
share_url: string | null
|
|
cover_image_url: string | null
|
|
|
|
// Performance metrics
|
|
view_count: number
|
|
like_count: number
|
|
comment_count: number
|
|
share_count: number
|
|
|
|
// Video duration in seconds
|
|
duration: number | null
|
|
|
|
// Publication date
|
|
published_at: string | null
|
|
|
|
// Campaign/deliverable linking
|
|
campaign_id: string | null
|
|
deliverable_id: string | null
|
|
|
|
// Timestamps
|
|
created_at: string
|
|
updated_at: string
|
|
|
|
// Relations
|
|
campaign?: Campaign
|
|
deliverable?: Deliverable
|
|
}
|
|
|
|
// TikTok sync log record
|
|
export interface TikTokSyncLog {
|
|
id: string
|
|
tiktok_account_id: string
|
|
user_id: string
|
|
|
|
// Sync type
|
|
sync_type: TikTokSyncType
|
|
|
|
// Sync result
|
|
status: TikTokSyncStatus
|
|
|
|
// Details
|
|
stats_synced: boolean
|
|
videos_synced: number
|
|
new_videos: number
|
|
|
|
// Error info
|
|
error_message: string | null
|
|
error_code: string | null
|
|
|
|
// Duration
|
|
started_at: string
|
|
completed_at: string | null
|
|
|
|
// Timestamps
|
|
created_at: string
|
|
}
|
|
|
|
// TikTok OAuth state (for CSRF protection)
|
|
export interface TikTokOAuthState {
|
|
user_id: string
|
|
redirect_url: string
|
|
created_at: number
|
|
}
|
|
|
|
// TikTok API response types
|
|
export interface TikTokUserInfo {
|
|
open_id: string
|
|
union_id: string
|
|
avatar_url: string
|
|
avatar_url_100: string
|
|
avatar_large_url: string
|
|
display_name: string
|
|
bio_description: string
|
|
profile_deep_link: string
|
|
is_verified: boolean
|
|
follower_count: number
|
|
following_count: number
|
|
likes_count: number
|
|
video_count: number
|
|
}
|
|
|
|
export interface TikTokVideoInfo {
|
|
id: string
|
|
title: string
|
|
video_description: string
|
|
duration: number
|
|
cover_image_url: string
|
|
share_url: string
|
|
embed_link: string
|
|
create_time: number
|
|
like_count: number
|
|
comment_count: number
|
|
share_count: number
|
|
view_count: number
|
|
}
|
|
|
|
// TikTok engagement metrics
|
|
export interface TikTokEngagementMetrics {
|
|
views: number
|
|
likes: number
|
|
comments: number
|
|
shares: number
|
|
engagementRate: number
|
|
averageViewDuration?: number
|
|
}
|
|
|
|
// TikTok campaign ROI
|
|
export interface TikTokCampaignROI {
|
|
campaignId: string
|
|
campaignName: string
|
|
totalCost: number
|
|
totalViews: number
|
|
totalEngagements: number
|
|
costPerView: number
|
|
costPerEngagement: number
|
|
videos: TikTokVideo[]
|
|
}
|
|
|
|
// TikTok follower growth
|
|
export interface TikTokFollowerGrowth {
|
|
period: '7d' | '30d' | '90d' | '1y'
|
|
startCount: number
|
|
endCount: number
|
|
change: number
|
|
changePercent: number
|
|
dailyAverage: number
|
|
}
|
|
|
|
// TikTok stats summary for dashboard
|
|
export interface TikTokStatsSummary {
|
|
currentFollowers: number
|
|
followerChange7d: number
|
|
followerChange30d: number
|
|
totalLikes: number
|
|
totalVideos: number
|
|
recentVideos: TikTokVideo[]
|
|
engagementRate: number
|
|
lastSynced: string | null
|
|
}
|
|
|
|
// Input types for TikTok operations
|
|
export interface LinkVideoToCampaignInput {
|
|
video_id: string
|
|
campaign_id?: string
|
|
deliverable_id?: string
|
|
}
|
|
|
|
export interface TikTokSyncInput {
|
|
account_id: string
|
|
sync_type?: TikTokSyncType
|
|
}
|
|
|
|
// TikTok status labels
|
|
export const TIKTOK_STATUS_LABELS: Record<TikTokAccountStatus, string> = {
|
|
active: 'Aktiv',
|
|
expired: 'Utgången',
|
|
revoked: 'Återkallad',
|
|
error: 'Fel'
|
|
}
|
|
|
|
export const TIKTOK_SYNC_TYPE_LABELS: Record<TikTokSyncType, string> = {
|
|
stats: 'Statistik',
|
|
videos: 'Videor',
|
|
full: 'Fullständig',
|
|
token_refresh: 'Token-förnyelse'
|
|
}
|
|
|
|
export const TIKTOK_SYNC_STATUS_LABELS: Record<TikTokSyncStatus, string> = {
|
|
started: 'Startad',
|
|
success: 'Lyckad',
|
|
partial: 'Delvis',
|
|
failed: 'Misslyckad'
|
|
}
|
|
|
|
// ============================================================
|
|
// Receipt Types (Kvittohantering)
|
|
// ============================================================
|
|
|
|
// 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
|
|
}
|
|
|
|
// Product registration (gift without receipt)
|
|
export interface ProductRegistration {
|
|
image_url: string
|
|
estimated_value: number
|
|
brand_name: string
|
|
description: string
|
|
// Gift classification inputs
|
|
has_motprestation: boolean
|
|
used_in_business: boolean
|
|
used_privately: boolean
|
|
is_simple_promo: boolean
|
|
}
|
|
|
|
// 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'
|
|
}
|
|
|
|
// ============================================================
|
|
// Briefing Types (Campaign Briefing Materials)
|
|
// ============================================================
|
|
|
|
// Briefing type
|
|
export type BriefingType = 'pdf' | 'link' | 'text'
|
|
|
|
// Briefing record
|
|
export interface Briefing {
|
|
id: string
|
|
campaign_id: string
|
|
user_id: string
|
|
|
|
briefing_type: BriefingType
|
|
title: string
|
|
|
|
// Content based on type:
|
|
// For 'pdf': file path in Supabase Storage
|
|
// For 'link': URL to external resource
|
|
// For 'text': null (use text_content instead)
|
|
content: string | null
|
|
|
|
// For 'text' type: the actual text content
|
|
text_content: string | null
|
|
|
|
// PDF metadata (only for 'pdf' type)
|
|
filename: string | null
|
|
file_size: number | null
|
|
mime_type: string | null
|
|
|
|
// Optional notes
|
|
notes: string | null
|
|
|
|
// Timestamps
|
|
created_at: string
|
|
updated_at: string
|
|
|
|
// Relations
|
|
campaign?: Campaign
|
|
}
|
|
|
|
// Input for creating a briefing
|
|
export interface CreateBriefingInput {
|
|
campaign_id: string
|
|
briefing_type: BriefingType
|
|
title: string
|
|
content?: string // File path or URL
|
|
text_content?: string // For text type
|
|
filename?: string
|
|
file_size?: number
|
|
mime_type?: string
|
|
notes?: string
|
|
}
|
|
|
|
// Swedish labels for briefing types
|
|
export const BRIEFING_TYPE_LABELS: Record<BriefingType, string> = {
|
|
pdf: 'PDF-dokument',
|
|
link: 'Länk',
|
|
text: 'Text'
|
|
}
|
|
|
|
// ============================================================
|
|
// VAT Declaration Types (Momsdeklaration)
|
|
// ============================================================
|
|
|
|
// VAT period type
|
|
export type VatPeriodType = 'monthly' | 'quarterly' | 'yearly'
|
|
|
|
// VAT declaration rutor (boxes) according to Swedish tax authority
|
|
export interface VatDeclarationRutor {
|
|
// Utgående moms (Output VAT)
|
|
ruta05: number // Utgående moms 25%
|
|
ruta06: number // Utgående moms 12%
|
|
ruta07: number // Utgående moms 6%
|
|
|
|
// Momspliktigt underlag (VAT-liable base amounts)
|
|
ruta10: number // Momspliktigt underlag 25%
|
|
ruta11: number // Momspliktigt underlag 12%
|
|
ruta12: number // Momspliktigt underlag 6%
|
|
|
|
// EU och export
|
|
ruta39: number // Försäljning av tjänster till annat EU-land (reverse charge)
|
|
ruta40: number // Export utanför EU
|
|
|
|
// Ingående moms (Input VAT)
|
|
ruta48: number // Ingående moms att dra av
|
|
|
|
// Moms att betala eller få tillbaka
|
|
ruta49: number // Moms att betala (positive) eller återfå (negative)
|
|
}
|
|
|
|
// VAT declaration response
|
|
export interface VatDeclaration {
|
|
period: {
|
|
type: VatPeriodType
|
|
year: number
|
|
period: number // 1-12 for monthly, 1-4 for quarterly, 1 for yearly
|
|
start: string // YYYY-MM-DD
|
|
end: string // YYYY-MM-DD
|
|
}
|
|
rutor: VatDeclarationRutor
|
|
// Supporting data
|
|
invoiceCount: number
|
|
transactionCount: number
|
|
// Breakdown by source
|
|
breakdown: {
|
|
invoices: {
|
|
ruta05: number
|
|
ruta06: number
|
|
ruta07: number
|
|
ruta10: number
|
|
ruta11: number
|
|
ruta12: number
|
|
ruta39: number
|
|
ruta40: number
|
|
}
|
|
transactions: {
|
|
ruta48: number // Ingående moms from categorized expenses
|
|
}
|
|
receipts: {
|
|
ruta48: number // Ingående moms from receipts
|
|
}
|
|
}
|
|
}
|
|
|
|
// VAT declaration request parameters
|
|
export interface VatDeclarationRequest {
|
|
periodType: VatPeriodType
|
|
year: number
|
|
period: number
|
|
}
|
|
|
|
// ============================================================
|
|
// NE Declaration Types (Enskild Firma - Sole Proprietorship)
|
|
// ============================================================
|
|
|
|
// NE-bilaga rutor (NE appendix boxes)
|
|
export interface NEDeclarationRutor {
|
|
R1: number // Försäljning med 25% moms (3000-3499 excl 3100)
|
|
R2: number // Momsfria intäkter (3100, 3970, 3980)
|
|
R3: number // Bil/bostadsförmån (3200)
|
|
R4: number // Ränteintäkter (8310-8330)
|
|
R5: number // Varuinköp (4000-4990)
|
|
R6: number // Övriga kostnader (5000-6990, 7970)
|
|
R7: number // Lönekostnader (7000-7699)
|
|
R8: number // Räntekostnader (8400-8499)
|
|
R9: number // Avskrivningar fastighet (7820)
|
|
R10: number // Avskrivningar övrigt (7700-7899 excl 7820)
|
|
R11: number // Årets resultat (beräknat)
|
|
}
|
|
|
|
// NE account mapping configuration
|
|
export interface NEAccountMapping {
|
|
ruta: keyof NEDeclarationRutor
|
|
description: string
|
|
accountRanges: Array<{
|
|
start: string
|
|
end: string
|
|
exclude?: string[]
|
|
}>
|
|
isExpense: boolean // true = debit normal, false = credit normal
|
|
}
|
|
|
|
// Gift item for NE declaration
|
|
export interface NEGiftItem {
|
|
id: string
|
|
date: string
|
|
brandName: string
|
|
description: string
|
|
marketValue: number
|
|
vatLiable: boolean
|
|
vatAmount: number
|
|
deductibleAsExpense: boolean
|
|
neIncomeRuta: NEIncomeRuta | null
|
|
neExpenseRuta: NEExpenseRuta | null
|
|
}
|
|
|
|
// Gift breakdown for NE declaration
|
|
export interface NEGiftBreakdown {
|
|
gifts: NEGiftItem[]
|
|
summary: {
|
|
r1Total: number // Gåvor med moms (motprestation)
|
|
r2Total: number // Gåvor utan moms
|
|
r6Total: number // Avdragsgilla gåvor
|
|
totalTaxable: number
|
|
totalDeductible: number
|
|
netTaxableIncome: number
|
|
}
|
|
}
|
|
|
|
// NE declaration response
|
|
export interface NEDeclaration {
|
|
fiscalYear: {
|
|
id: string
|
|
name: string
|
|
start: string
|
|
end: string
|
|
isClosed: boolean
|
|
}
|
|
rutor: NEDeclarationRutor
|
|
// Detailed breakdown per ruta
|
|
breakdown: Record<keyof NEDeclarationRutor, {
|
|
accounts: Array<{
|
|
accountNumber: string
|
|
accountName: string
|
|
amount: number
|
|
}>
|
|
total: number
|
|
}>
|
|
// Gift breakdown (included in rutor via journal entries)
|
|
giftBreakdown?: NEGiftBreakdown
|
|
// Company info for SRU
|
|
companyInfo: {
|
|
companyName: string
|
|
orgNumber: string | null
|
|
}
|
|
// Warnings
|
|
warnings: string[]
|
|
}
|
|
|
|
// SRU file format types
|
|
export interface SRURecord {
|
|
fieldCode: string
|
|
value: string | number
|
|
}
|
|
|
|
export interface SRUFile {
|
|
records: SRURecord[]
|
|
generatedAt: string
|
|
}
|
|
|
|
// Labels for NE rutor
|
|
export const NE_RUTA_LABELS: Record<keyof NEDeclarationRutor, string> = {
|
|
R1: 'Försäljning med moms (25%)',
|
|
R2: 'Momsfria intäkter',
|
|
R3: 'Bil/bostadsförmån',
|
|
R4: 'Ränteintäkter',
|
|
R5: 'Varuinköp',
|
|
R6: 'Övriga kostnader',
|
|
R7: 'Lönekostnader',
|
|
R8: 'Räntekostnader',
|
|
R9: 'Avskrivningar fastighet',
|
|
R10: 'Avskrivningar övriga tillgångar',
|
|
R11: 'Årets resultat'
|
|
}
|
|
|
|
// Labels for VAT rutor
|
|
export const VAT_RUTA_LABELS: Record<keyof VatDeclarationRutor, string> = {
|
|
ruta05: 'Utgående moms 25%',
|
|
ruta06: 'Utgående moms 12%',
|
|
ruta07: 'Utgående moms 6%',
|
|
ruta10: 'Momspliktigt underlag 25%',
|
|
ruta11: 'Momspliktigt underlag 12%',
|
|
ruta12: 'Momspliktigt underlag 6%',
|
|
ruta39: 'Försäljning av tjänster till EU-land',
|
|
ruta40: 'Export utanför EU',
|
|
ruta48: 'Ingående moms att dra av',
|
|
ruta49: 'Moms att betala/återfå'
|
|
}
|
|
|
|
// ============================================================
|
|
// Invoice Reminder Types (Betalningspåminnelser)
|
|
// ============================================================
|
|
|
|
// Response type from customer action
|
|
export type ReminderResponseType = 'marked_paid' | 'disputed'
|
|
|
|
// Invoice reminder record
|
|
export interface InvoiceReminder {
|
|
id: string
|
|
invoice_id: string
|
|
user_id: string
|
|
reminder_level: 1 | 2 | 3
|
|
sent_at: string
|
|
email_to: string
|
|
response_type: ReminderResponseType | null
|
|
response_at: string | null
|
|
action_token: string
|
|
action_token_used: boolean
|
|
created_at: string
|
|
}
|
|
|
|
// Swedish labels for reminder levels
|
|
export const REMINDER_LEVEL_LABELS: Record<1 | 2 | 3, string> = {
|
|
1: 'Vänlig påminnelse',
|
|
2: 'Andra påminnelsen',
|
|
3: 'Slutlig påminnelse'
|
|
}
|
|
|
|
// Reminder level descriptions
|
|
export const REMINDER_LEVEL_DESCRIPTIONS: Record<1 | 2 | 3, string> = {
|
|
1: '15 dagar efter förfallodatum',
|
|
2: '30 dagar efter förfallodatum',
|
|
3: '45 dagar efter förfallodatum'
|
|
}
|