feat: UI polish, lint fixes, onboarding redesign, help page expansion, and test improvements
Broad update across dashboard pages, components, extensions, and lib code. Includes ESLint config additions, onboarding flow redesign, settings page refactor, help page content expansion, dead code removal, and test mock fixes. Adds dev docs and public assets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
02ebe50f52
commit
091d043c85
@@ -451,7 +451,7 @@ function validateVatBreakdown(data: any): VatBreakdownItem[] {
|
||||
*/
|
||||
export function validateExtractionConsistency(
|
||||
extraction: ReceiptExtractionResult | InvoiceExtractionResult,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
_type: 'receipt' | 'invoice'
|
||||
): ConsistencyResult {
|
||||
const issues: string[] = []
|
||||
|
||||
@@ -13,9 +13,6 @@ const isoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Expected YYYY-MM-DD dat
|
||||
/** BAS account number — always a string of 4 digits */
|
||||
const accountNumber = z.string().regex(/^\d{4}$/, 'Account number must be exactly 4 digits')
|
||||
|
||||
/** Positive monetary amount (> 0) */
|
||||
const positiveAmount = z.number().positive()
|
||||
|
||||
/** Non-negative monetary amount (>= 0) */
|
||||
const nonNegativeAmount = z.number().nonnegative()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getBASReference, ACCOUNT_CLASS_LABELS } from './bas-reference'
|
||||
|
||||
export type AccountType = 'asset' | 'liability' | 'equity' | 'revenue' | 'expense' | 'untaxed_reserves'
|
||||
|
||||
export interface AccountDescription {
|
||||
@@ -332,8 +334,6 @@ export function getAccountDescription(accountNumber: string): AccountDescription
|
||||
|
||||
// Fall back to BAS reference data for accounts not in the hardcoded list
|
||||
try {
|
||||
// Dynamic import avoided — use lazy require pattern
|
||||
const { getBASReference, ACCOUNT_CLASS_LABELS } = require('./bas-reference')
|
||||
const ref = getBASReference(accountNumber)
|
||||
if (ref) {
|
||||
const classLabel = ACCOUNT_CLASS_LABELS[ref.account_class] || ''
|
||||
|
||||
@@ -173,10 +173,8 @@ export async function previewCurrencyRevaluation(
|
||||
let credit1510 = 0 // Receivable loss (revalue down)
|
||||
let debit2440 = 0 // Payable gain (liability shrank)
|
||||
let credit2440 = 0 // Payable loss (liability grew)
|
||||
let debit3960 = 0 // Placeholder — we won't debit 3960
|
||||
let credit3960 = 0 // Gains
|
||||
let debit7960 = 0 // Losses
|
||||
let credit7960 = 0 // Placeholder — we won't credit 7960
|
||||
|
||||
for (const item of items) {
|
||||
if (item.type === 'receivable') {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createJournalEntry, findFiscalPeriod } from './engine'
|
||||
import { resolveSekAmount, buildCurrencyMetadata } from './currency-utils'
|
||||
import { generateSalesVatLines, generateReverseChargeLines } from './vat-entries'
|
||||
import { generateSalesVatLines } from './vat-entries'
|
||||
import { getVatTreatmentForRate } from '@/lib/invoices/vat-rules'
|
||||
import { createLogger } from '@/lib/logger'
|
||||
import type { SupabaseClient } from '@supabase/supabase-js'
|
||||
|
||||
@@ -2,14 +2,11 @@ import type { SupabaseClient } from '@supabase/supabase-js'
|
||||
import {
|
||||
generateInputVatLine,
|
||||
generateReverseChargeLines,
|
||||
extractNetAmount,
|
||||
extractVatAmount,
|
||||
} from './vat-entries'
|
||||
import { findMatchingTemplates, buildMappingResultFromTemplate } from './booking-templates'
|
||||
import type {
|
||||
MappingRule,
|
||||
MappingResult,
|
||||
RiskLevel,
|
||||
Transaction,
|
||||
EntityType,
|
||||
VatJournalLine,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createJournalEntry, findFiscalPeriod } from './engine'
|
||||
import { resolveSekAmount, buildCurrencyMetadata } from './currency-utils'
|
||||
import { generateInputVatLine, generateReverseChargeLines, extractNetAmount, extractVatAmount } from './vat-entries'
|
||||
import { extractNetAmount, extractVatAmount } from './vat-entries'
|
||||
import { createLogger } from '@/lib/logger'
|
||||
import type { SupabaseClient } from '@supabase/supabase-js'
|
||||
import type {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { SupabaseClient } from '@supabase/supabase-js'
|
||||
import { createServiceClient } from '@/lib/supabase/server'
|
||||
import { eventBus } from '@/lib/events'
|
||||
import type { DocumentAttachment, CreateDocumentAttachmentInput, DocumentUploadSource } from '@/types'
|
||||
import type { DocumentAttachment, DocumentUploadSource } from '@/types'
|
||||
|
||||
/**
|
||||
* Document Service - WORM-style document archive
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { matchDocumentToTransactions } from '../document-matcher'
|
||||
import { makeInvoiceInboxItem, makeTransaction } from '@/tests/helpers'
|
||||
import type { InvoiceExtractionResult, ReceiptExtractionResult, Transaction } from '@/types'
|
||||
import type { InvoiceExtractionResult, ReceiptExtractionResult } from '@/types'
|
||||
|
||||
describe('matchDocumentToTransactions', () => {
|
||||
const mockSupabase = {} as never // Not used when candidateTransactions is provided
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { createMockSupabase } from '@/tests/helpers'
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { resolve, join, dirname } from 'path'
|
||||
import { resolve, join } from 'path'
|
||||
import { readdirSync, readFileSync } from 'fs'
|
||||
|
||||
/**
|
||||
@@ -74,6 +74,7 @@ describe('sectors registry', () => {
|
||||
})
|
||||
|
||||
it('getSector returns undefined for unknown slug', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const sector = getSector('invalid' as any)
|
||||
expect(sector).toBeUndefined()
|
||||
})
|
||||
|
||||
@@ -355,7 +355,7 @@ async function importVouchers(
|
||||
p_series: voucherSeries,
|
||||
})
|
||||
|
||||
let currentVoucherNumber = (startNumber as number) || 1
|
||||
const currentVoucherNumber = (startNumber as number) || 1
|
||||
|
||||
// Batch insert journal entries (in chunks of 100)
|
||||
const BATCH_SIZE = 100
|
||||
|
||||
@@ -20,6 +20,7 @@ function makeBuilder() {
|
||||
function makeClient() {
|
||||
return {
|
||||
from: vi.fn().mockImplementation(() => makeBuilder()),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ function makeBuilder() {
|
||||
function makeClient() {
|
||||
return {
|
||||
from: vi.fn().mockImplementation(() => makeBuilder()),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { TrialBalanceRow } from '@/types'
|
||||
|
||||
const mockTrialBalance = vi.mocked(generateTrialBalance)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const supabase = {} as any
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import JSZip from 'jszip'
|
||||
import { generateFullArchive } from '../full-archive-export'
|
||||
|
||||
@@ -21,6 +21,7 @@ function makeClient() {
|
||||
return {
|
||||
from: vi.fn().mockImplementation(() => makeBuilder()),
|
||||
rpc: vi.fn().mockImplementation(async () => results[resultIdx++] ?? { data: null, error: null }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { TrialBalanceRow } from '@/types'
|
||||
|
||||
const mockTrialBalance = vi.mocked(generateTrialBalance)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const supabase = {} as any
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -20,6 +20,7 @@ function makeBuilder() {
|
||||
function makeClient() {
|
||||
return {
|
||||
from: vi.fn().mockImplementation(() => makeBuilder()),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ function makeBuilder() {
|
||||
function makeClient() {
|
||||
return {
|
||||
from: vi.fn().mockImplementation(() => makeBuilder()),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ function makeBuilder() {
|
||||
function makeClient() {
|
||||
return {
|
||||
from: vi.fn().mockImplementation(() => makeBuilder()),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ function makeBuilder() {
|
||||
function makeClient() {
|
||||
return {
|
||||
from: vi.fn().mockImplementation(() => makeBuilder()),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ function makeClient() {
|
||||
return {
|
||||
from: vi.fn().mockImplementation(() => makeBuilder()),
|
||||
rpc: vi.fn().mockImplementation(async () => results[resultIdx++] ?? { data: null, error: null }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ function makeBuilder() {
|
||||
function makeClient() {
|
||||
return {
|
||||
from: vi.fn().mockImplementation(() => makeBuilder()),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { INK2_ACCOUNT_MAPPINGS, isAccountInMapping } from '../ink2-engine'
|
||||
import type { INK2AccountMapping, INK2SRUCode } from '../types'
|
||||
import type { INK2SRUCode } from '../types'
|
||||
|
||||
/**
|
||||
* Helper to find which SRU code an account maps to
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { NE_ACCOUNT_MAPPINGS } from '../ne-engine'
|
||||
import type { NEAccountMapping } from '@/lib/reports/ne-bilaga/types'
|
||||
|
||||
/**
|
||||
* Helper to check if an account falls into a specific ruta
|
||||
|
||||
@@ -137,8 +137,6 @@ export function calculateABTax(
|
||||
|
||||
// Arbetsgivaravgifter on salaries (already included in salary costs usually)
|
||||
// This is just for reference
|
||||
const arbetsgivaravgifter = salaryCostsYTD * TAX_RATES.arbetsgivaravgifter
|
||||
|
||||
const totalTax = bolagsskatt + momsFromUnpaidInvoices
|
||||
|
||||
return {
|
||||
|
||||
@@ -214,7 +214,6 @@ export const TAX_DEADLINE_CONFIGS: TaxDeadlineConfig[] = [
|
||||
const dl = getDeadline(fyEndYear)
|
||||
if (dl.year === year) {
|
||||
// Compute the FY start year
|
||||
const fyStartYear = fyEndMonth === 12 ? fyEndYear : fyEndYear - (fyEndMonth < settings.fiscal_year_start_month ? 0 : 0)
|
||||
const fyStart = fyEndMonth === 12 ? fyEndYear : fyEndYear
|
||||
const periodLabel = fyEndMonth === 12
|
||||
? `${fyEndYear}`
|
||||
@@ -249,14 +248,12 @@ export const TAX_DEADLINE_CONFIGS: TaxDeadlineConfig[] = [
|
||||
|
||||
// 7 months after FY end per ÅRL 8:3
|
||||
// Deadline month (0-indexed): ((fyEndMonth - 1) + 7) % 12
|
||||
const deadlineMonth0 = ((fyEndMonth - 1) + 7) % 12
|
||||
// Last day of the deadline month
|
||||
// Determine which year the deadline falls in
|
||||
const wrapsYear = fyEndMonth > 5 // Jun+ wraps into next year
|
||||
const _wrapsYear = fyEndMonth > 5 // Jun+ wraps into next year
|
||||
// For calendar year (Dec end): deadline Jul 31 same year+1
|
||||
// The FY ending in `year` produces a deadline:
|
||||
const fyEndYear = year - 1 // By default we show deadline for the FY that ended in year-1
|
||||
const deadlineYear = wrapsYear ? fyEndYear + 1 + 1 : fyEndYear + 1
|
||||
const _fyEndYear = year - 1 // By default we show deadline for the FY that ended in year-1
|
||||
// Simpler: compute from a concrete FY end date
|
||||
// FY ends: fyEndMonth (1-indexed), last day, in some year.
|
||||
// We want the deadline that falls in `year`.
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
|
||||
import { SupabaseClient } from '@supabase/supabase-js'
|
||||
import { createLogger } from '@/lib/logger'
|
||||
import type { TaxDeadlineType, DeadlineStatus, CreateDeadlineInput } from '@/types'
|
||||
import type { TaxDeadlineType, DeadlineStatus } from '@/types'
|
||||
|
||||
const log = createLogger('deadline-generator')
|
||||
import {
|
||||
TAX_DEADLINE_CONFIGS,
|
||||
getApplicableDeadlineConfigs,
|
||||
type CompanySettingsForDeadlines,
|
||||
type DeadlineInstance,
|
||||
@@ -180,7 +179,7 @@ export async function generateTaxDeadlinesForUser(
|
||||
*/
|
||||
function createLinkedReportPeriod(
|
||||
instance: DeadlineInstance,
|
||||
type: TaxDeadlineType
|
||||
_type: TaxDeadlineType
|
||||
): Record<string, unknown> | null {
|
||||
const period = instance.period
|
||||
|
||||
|
||||
Reference in New Issue
Block a user