Bug/gh issues fiz (#1103)

* refactor: optimize page loading and data fetching

* fix: resolve recurring production runtime errors

* feat: add MCP company and customer updates

* fix: handle year-end tax adjustments

* feat: harden annual report compliance

* fix: expand invoice logo and font support

* fix: sanitize API route error responses

* fix: sanitize user-facing error messages

* feat: persist onboarding and tax assessment notices

* fix: reduce cloud backup audit churn

* feat: refine invoice editor layout

* fix: show saved tax adjustments in INK2

* fix: complete annual report API mappings

* docs: record operational safeguards and decisions

* fix: harden annual report review findings

* fix: adjust column span for description based on VAT registration

* New css class name
This commit is contained in:
Mattsson
2026-07-21 23:00:15 +02:00
committed by GitHub
parent 702512437a
commit e11f70b347
467 changed files with 17569 additions and 2221 deletions
@@ -121,7 +121,7 @@ describe('POST /api/transactions/[id]/ignore', () => {
const { status, body } = await parseJsonResponse(response)
expect(status).toBe(500)
expect(body).toEqual({ error: 'db down' })
expect(body).toEqual({ error: 'Något gick fel. Försök igen.' })
})
})
@@ -174,6 +174,6 @@ describe('DELETE /api/transactions/[id]/ignore', () => {
const { status, body } = await parseJsonResponse(response)
expect(status).toBe(500)
expect(body).toEqual({ error: 'db down' })
expect(body).toEqual({ error: 'Något gick fel. Försök igen.' })
})
})
+3 -2
View File
@@ -1,5 +1,6 @@
import { NextResponse } from 'next/server'
import { withRouteContext } from '@/lib/api/with-route-context'
import { getErrorMessage as getUserErrorMessage } from '@/lib/errors/get-error-message'
/**
* POST /api/transactions/[id]/ignore
@@ -47,7 +48,7 @@ export const POST = withRouteContext<{ params: Promise<{ id: string }> }>(
.eq('company_id', companyId)
if (updateError) {
return NextResponse.json({ error: updateError.message }, { status: 500 })
return NextResponse.json({ error: getUserErrorMessage(updateError) }, { status: 500 })
}
return NextResponse.json({ success: true })
@@ -74,7 +75,7 @@ export const DELETE = withRouteContext<{ params: Promise<{ id: string }> }>(
.eq('company_id', companyId)
if (updateError) {
return NextResponse.json({ error: updateError.message }, { status: 500 })
return NextResponse.json({ error: getUserErrorMessage(updateError) }, { status: 500 })
}
return NextResponse.json({ success: true })
@@ -6,6 +6,7 @@ import { errorResponseFromCode } from '@/lib/errors/get-structured-error'
import { eventBus } from '@/lib/events/bus'
import { ensureInitialized } from '@/lib/init'
import type { Invoice, SupplierInvoice, Transaction } from '@/types'
import { getErrorMessage as getUserErrorMessage } from '@/lib/errors/get-error-message'
ensureInitialized()
@@ -81,7 +82,7 @@ export const POST = withRouteContext(
txLog.error('match_batch_allocate RPC error', error)
return errorResponseFromCode('BATCH_RPC_FAILED', txLog, {
requestId,
details: { message: error.message },
details: { message: getUserErrorMessage(error) },
})
}
@@ -17,6 +17,7 @@ import { logMatchEvent } from '@/lib/invoices/match-log'
import { eventBus } from '@/lib/events/bus'
import { ensureInitialized } from '@/lib/init'
import type { SupplierInvoice, SupplierInvoiceItem, Transaction } from '@/types'
import { getErrorMessage as getUserErrorMessage } from '@/lib/errors/get-error-message'
ensureInitialized()
@@ -320,7 +321,7 @@ export const POST = withRouteContext(
}
return errorResponseFromCode('MATCH_SI_JE_FAILED', txLog, {
requestId,
details: { reason: err instanceof Error ? err.message : 'unknown' },
details: { reason: err instanceof Error ? getUserErrorMessage(err) : 'unknown' },
})
}
@@ -121,7 +121,7 @@ describe('POST /api/transactions/[id]/uncategorize', () => {
const { status, body } = await parseJsonResponse(response)
expect(status).toBe(500)
expect(body).toEqual({ error: 'Period is locked' })
expect(body).toEqual({ error: 'Kunde inte hantera transaktionen. Försök igen.' })
})
it('returns 200 and reverses entry on success', async () => {
@@ -3,6 +3,7 @@ import { reverseEntry } from '@/lib/bookkeeping/engine'
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
import { ensureInitialized } from '@/lib/init'
import { withRouteContext } from '@/lib/api/with-route-context'
import { getErrorMessage } from '@/lib/errors/get-error-message'
ensureInitialized()
@@ -49,8 +50,10 @@ export const POST = withRouteContext<{ params: Promise<{ id: string }> }>(
} catch (err) {
const typed = bookkeepingErrorResponse(err)
if (typed) return typed
const message = err instanceof Error ? err.message : 'Reversal failed'
return NextResponse.json({ error: message }, { status: 500 })
return NextResponse.json(
{ error: getErrorMessage(err, { context: 'transaction' }) },
{ status: 500 },
)
}
// Reset transaction categorization
+1 -1
View File
@@ -187,6 +187,6 @@ describe('GET /api/transactions', () => {
const { status, body } = await parseJsonResponse<{ error: string }>(response)
expect(status).toBe(500)
expect(body.error).toBe('boom')
expect(body.error).toBe('Något gick fel. Försök igen.')
})
})
+3 -2
View File
@@ -14,6 +14,7 @@ import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
import { eventBus } from '@/lib/events/bus'
import { ensureInitialized } from '@/lib/init'
import type { BookingTemplateLibraryLine, Transaction } from '@/types'
import { getErrorMessage as getUserErrorMessage } from '@/lib/errors/get-error-message'
ensureInitialized()
@@ -108,7 +109,7 @@ export const POST = withRouteContext(
opLog.error('chart_of_accounts lookup failed', accountsError)
return errorResponseFromCode('BULK_BOOK_RPC_FAILED', opLog, {
requestId,
details: { message: accountsError.message },
details: { message: getUserErrorMessage(accountsError) },
})
}
const validSet = new Set(
@@ -283,7 +284,7 @@ export const POST = withRouteContext(
opLog.error('bulk_book_transactions RPC error', error)
return errorResponseFromCode('BULK_BOOK_RPC_FAILED', opLog, {
requestId,
details: { message: error.message },
details: { message: getUserErrorMessage(error) },
})
}
+3 -2
View File
@@ -5,6 +5,7 @@ import { scopeTransactionsToAccount } from '@/lib/reconciliation/bank-reconcilia
import { withRouteContext } from '@/lib/api/with-route-context'
import { validateBody } from '@/lib/api/validate'
import { CreateTransactionSchema } from '@/lib/api/schemas'
import { getErrorMessage as getUserErrorMessage } from '@/lib/errors/get-error-message'
const MAX_ROWS = 500
@@ -98,7 +99,7 @@ export async function GET(request: Request) {
const { data, error } = await query
if (error) {
return NextResponse.json({ error: error.message }, { status: 500 })
return NextResponse.json({ error: getUserErrorMessage(error) }, { status: 500 })
}
const rows = data || []
@@ -147,7 +148,7 @@ export const POST = withRouteContext(
// is invalid input, not a server fault: surface it as 400 with the PG
// code so the client maps it to a friendly message.
return NextResponse.json(
{ error: error.message, code: error.code, type: 'database_error' },
{ error: getUserErrorMessage(error), code: error.code, type: 'database_error' },
{ status: 400 },
)
}