Refactor bookkeeping error handling and introduce new error classes (#356)
- Introduced new error classes for better error categorization: - JournalEntryNotBalancedError - FiscalPeriodNotFoundError - EntryDateOutsideFiscalPeriodError - JournalEntryNotFoundError - CannotReverseNonPostedError - CannotCorrectNonPostedError - EntryAlreadyReversedError - CurrencyRevaluationAlreadyExistsError - InvalidMappingResultError - BookkeepingDatabaseError - Updated existing functions in engine.ts and transaction-entries.ts to throw specific errors instead of generic ones. - Enhanced error response handling in get-error-message.ts to provide localized messages for new error types. - Added unit tests for new error classes and error handling functions to ensure correctness and coverage.
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
previewCurrencyRevaluation,
|
||||
executeCurrencyRevaluation,
|
||||
} from '@/lib/bookkeeping/currency-revaluation'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
import { requireWritePermission } from '@/lib/auth/require-write'
|
||||
|
||||
@@ -40,6 +41,8 @@ export async function GET(
|
||||
const preview = await previewCurrencyRevaluation(supabase, companyId, period.period_end)
|
||||
return NextResponse.json({ data: preview })
|
||||
} catch (err) {
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
return NextResponse.json(
|
||||
{ error: err instanceof Error ? err.message : 'Failed to preview currency revaluation' },
|
||||
{ status: 400 }
|
||||
@@ -92,6 +95,8 @@ export async function POST(
|
||||
|
||||
return NextResponse.json({ data: result })
|
||||
} catch (err) {
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
return NextResponse.json(
|
||||
{ error: err instanceof Error ? err.message : 'Failed to execute currency revaluation' },
|
||||
{ status: 400 }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { correctEntry } from '@/lib/core/bookkeeping/storno-service'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { ensureInitialized } from '@/lib/init'
|
||||
import { validateBody } from '@/lib/api/validate'
|
||||
import { CorrectJournalEntrySchema } from '@/lib/api/schemas'
|
||||
@@ -35,9 +35,8 @@ export async function POST(
|
||||
const result = await correctEntry(supabase, companyId, user.id, id, body.lines)
|
||||
return NextResponse.json({ data: result })
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
return NextResponse.json(
|
||||
{ error: err instanceof Error ? err.message : 'Failed to correct entry' },
|
||||
{ status: 400 }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { reverseEntry } from '@/lib/bookkeeping/engine'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { ensureInitialized } from '@/lib/init'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
import { requireWritePermission } from '@/lib/auth/require-write'
|
||||
@@ -29,9 +29,8 @@ export async function POST(
|
||||
const reversalEntry = await reverseEntry(supabase, companyId, user.id, id)
|
||||
return NextResponse.json({ data: reversalEntry })
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
return NextResponse.json(
|
||||
{ error: err instanceof Error ? err.message : 'Failed to reverse entry' },
|
||||
{ status: 400 }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createJournalEntry } from '@/lib/bookkeeping/engine'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { ensureInitialized } from '@/lib/init'
|
||||
import { validateBody } from '@/lib/api/validate'
|
||||
import { CreateJournalEntrySchema } from '@/lib/api/schemas'
|
||||
@@ -124,9 +124,8 @@ export async function POST(request: Request) {
|
||||
const entry = await createJournalEntry(supabase, companyId, user.id, body)
|
||||
return NextResponse.json({ data: entry })
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
return NextResponse.json(
|
||||
{ error: err instanceof Error ? err.message : 'Failed to create journal entry' },
|
||||
{ status: 400 }
|
||||
|
||||
@@ -6,7 +6,7 @@ import { OpeningBalanceExecuteSchema } from '@/lib/api/schemas'
|
||||
import { requireWritePermission } from '@/lib/auth/require-write'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
import { createJournalEntry } from '@/lib/bookkeeping/engine'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { getBASReference } from '@/lib/bookkeeping/bas-reference'
|
||||
import { fetchAllRows } from '@/lib/supabase/fetch-all'
|
||||
import type { CreateJournalEntryLineInput } from '@/types'
|
||||
@@ -242,9 +242,8 @@ export async function POST(request: Request) {
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(error)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(error)
|
||||
if (typed) return typed
|
||||
console.error('Opening balance execute error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : 'Importen misslyckades' },
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
createInvoiceCashEntry,
|
||||
} from '@/lib/bookkeeping/invoice-entries'
|
||||
import { createJournalEntry, findFiscalPeriod } from '@/lib/bookkeeping/engine'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { MarkInvoicePaidSchema } from '@/lib/api/schemas'
|
||||
import { ensureInitialized } from '@/lib/init'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
@@ -161,9 +161,8 @@ export async function POST(
|
||||
journalEntryId = journalEntry?.id ?? null
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
console.error('Failed to create payment journal entry:', err)
|
||||
return NextResponse.json(
|
||||
{ error: 'Kunde inte bokföra betalningen' },
|
||||
|
||||
@@ -16,7 +16,11 @@ import {
|
||||
createInvoiceJournalEntry,
|
||||
} from '@/lib/bookkeeping/invoice-entries'
|
||||
import { reverseEntry } from '@/lib/bookkeeping/engine'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import {
|
||||
AccountsNotInChartError,
|
||||
bookkeepingErrorResponse,
|
||||
isBookkeepingError,
|
||||
} from '@/lib/bookkeeping/errors'
|
||||
import { getEmailService } from '@/lib/email/service'
|
||||
import {
|
||||
generateInvoiceEmailHtml,
|
||||
@@ -220,7 +224,7 @@ async function commitCategorizeTransaction(
|
||||
journalEntryId = journalEntry.id
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) throw err
|
||||
if (isBookkeepingError(err)) throw err
|
||||
log.error('Failed to create journal entry:', err)
|
||||
return { error: err instanceof Error ? err.message : 'Failed to create journal entry', status: 500 }
|
||||
}
|
||||
@@ -787,7 +791,7 @@ async function commitMatchTransactionInvoice(
|
||||
journalEntryId = je?.id ?? null
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) throw err
|
||||
if (isBookkeepingError(err)) throw err
|
||||
log.error('Failed to create match journal entry:', err)
|
||||
}
|
||||
|
||||
@@ -915,9 +919,8 @@ export async function POST(
|
||||
return NextResponse.json({ error: 'Unknown operation type' }, { status: 400 })
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
throw err
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ensureInitialized } from '@/lib/init'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
import { requireWritePermission } from '@/lib/auth/require-write'
|
||||
import { reverseEntry } from '@/lib/bookkeeping/engine'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse, EntryAlreadyReversedError } from '@/lib/bookkeeping/errors'
|
||||
|
||||
ensureInitialized()
|
||||
|
||||
@@ -62,14 +62,12 @@ export async function POST(
|
||||
try {
|
||||
await reverseEntry(supabase, companyId, user.id, entryId)
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
// Entry may already be reversed — continue
|
||||
if (err instanceof EntryAlreadyReversedError) continue
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
const msg = err instanceof Error ? err.message : ''
|
||||
if (!msg.includes('already reversed')) {
|
||||
return NextResponse.json({ error: `Kunde inte makulera verifikation: ${msg}` }, { status: 500 })
|
||||
}
|
||||
return NextResponse.json({ error: `Kunde inte makulera verifikation: ${msg}` }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { NextResponse } from 'next/server'
|
||||
import { eventBus } from '@/lib/events'
|
||||
import { ensureInitialized } from '@/lib/init'
|
||||
import { createSupplierCreditNoteEntry } from '@/lib/bookkeeping/supplier-invoice-entries'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
import { requireWritePermission } from '@/lib/auth/require-write'
|
||||
import type { SupplierInvoice, SupplierInvoiceItem, AccountingMethod } from '@/types'
|
||||
@@ -138,9 +138,8 @@ export async function POST(
|
||||
// momsdeklaration-integrity concern as the POST-route rollback.
|
||||
await supabase.from('supplier_invoices').delete().eq('id', creditNote.id).eq('company_id', companyId)
|
||||
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
console.error('Failed to create credit note journal entry:', err)
|
||||
return NextResponse.json(
|
||||
{ error: 'Kunde inte bokföra kreditfakturan — försök igen eller ändra datum om perioden är låst.' },
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
createSupplierInvoicePaymentEntry,
|
||||
createSupplierInvoiceCashEntry,
|
||||
} from '@/lib/bookkeeping/supplier-invoice-entries'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { validateBody } from '@/lib/api/validate'
|
||||
import { MarkSupplierInvoicePaidSchema } from '@/lib/api/schemas'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
@@ -99,9 +99,8 @@ export async function POST(
|
||||
if (journalEntry) journalEntryId = journalEntry.id
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
console.error('Failed to create payment journal entry:', err)
|
||||
return NextResponse.json(
|
||||
{ error: 'Kunde inte bokföra betalningen' },
|
||||
|
||||
@@ -30,6 +30,8 @@ vi.mock('@/lib/bookkeeping/engine', () => ({
|
||||
reverseEntry: (...args: unknown[]) => mockReverseEntry(...args),
|
||||
}))
|
||||
|
||||
import { CannotReverseNonPostedError } from '@/lib/bookkeeping/errors'
|
||||
|
||||
import { eventBus } from '@/lib/events'
|
||||
|
||||
import { POST } from '../route'
|
||||
@@ -222,7 +224,7 @@ describe('POST /api/supplier-invoices/[id]/uncredit', () => {
|
||||
data: { id: 'credit-1', registration_journal_entry_id: 'je-credit' },
|
||||
error: null,
|
||||
})
|
||||
mockReverseEntry.mockRejectedValue(new Error('Can only reverse posted entries'))
|
||||
mockReverseEntry.mockRejectedValue(new CannotReverseNonPostedError('draft'))
|
||||
enqueue({ data: null, error: null })
|
||||
enqueue({
|
||||
data: { ...original, status: 'approved', remaining_amount: 5000 },
|
||||
|
||||
@@ -3,7 +3,11 @@ import { NextResponse } from 'next/server'
|
||||
import { eventBus } from '@/lib/events'
|
||||
import { ensureInitialized } from '@/lib/init'
|
||||
import { reverseEntry } from '@/lib/bookkeeping/engine'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import {
|
||||
bookkeepingErrorResponse,
|
||||
CannotReverseNonPostedError,
|
||||
EntryAlreadyReversedError,
|
||||
} from '@/lib/bookkeeping/errors'
|
||||
import { getErrorMessage } from '@/lib/errors/get-error-message'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
import { requireWritePermission } from '@/lib/auth/require-write'
|
||||
@@ -69,16 +73,12 @@ export async function POST(
|
||||
)
|
||||
reversalEntryId = reversal.id
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err)
|
||||
// Already reversed (manually or by another concurrent uncredit) — fine, continue.
|
||||
if (
|
||||
/Can only reverse posted entries/i.test(msg) ||
|
||||
/already reversed by a concurrent operation/i.test(msg)
|
||||
) {
|
||||
if (err instanceof CannotReverseNonPostedError || err instanceof EntryAlreadyReversedError) {
|
||||
// proceed to row cleanup
|
||||
} else if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
} else {
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
// Period lock and similar trigger errors — surface a clear Swedish message
|
||||
// so the user knows WHY the action failed (per project's error-UX guidelines).
|
||||
return NextResponse.json(
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createClient } from '@/lib/supabase/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { eventBus } from '@/lib/events'
|
||||
import { createSupplierInvoiceRegistrationEntry } from '@/lib/bookkeeping/supplier-invoice-entries'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { ensureInitialized } from '@/lib/init'
|
||||
import { validateBody } from '@/lib/api/validate'
|
||||
import { CreateSupplierInvoiceSchema } from '@/lib/api/schemas'
|
||||
@@ -272,9 +272,8 @@ export async function POST(request: Request) {
|
||||
// which silently understates the momsdeklaration for the period.
|
||||
await supabase.from('supplier_invoices').delete().eq('id', invoice.id).eq('company_id', companyId)
|
||||
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
console.error('Failed to create registration journal entry:', err)
|
||||
return NextResponse.json(
|
||||
{ error: 'Kunde inte bokföra leverantörsfakturan — försök igen eller ändra datum om perioden är låst.' },
|
||||
|
||||
@@ -3,7 +3,7 @@ import { NextResponse } from 'next/server'
|
||||
import { eventBus } from '@/lib/events'
|
||||
import { ensureInitialized } from '@/lib/init'
|
||||
import { createJournalEntry } from '@/lib/bookkeeping/engine'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { validateBody } from '@/lib/api/validate'
|
||||
import { BookTransactionSchema } from '@/lib/api/schemas'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
@@ -66,9 +66,8 @@ export async function POST(
|
||||
lines,
|
||||
})
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
return NextResponse.json(
|
||||
{ error: err instanceof Error ? err.message : 'Failed to create journal entry' },
|
||||
{ status: 400 }
|
||||
|
||||
@@ -5,6 +5,8 @@ import { ensureInitialized } from '@/lib/init'
|
||||
import { buildMappingResultFromCategory } from '@/lib/bookkeeping/category-mapping'
|
||||
import { getTemplateById, buildMappingResultFromTemplate, validateTemplateForEntity } from '@/lib/bookkeeping/booking-templates'
|
||||
import { createTransactionJournalEntry } from '@/lib/bookkeeping/transaction-entries'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { getErrorMessage } from '@/lib/errors/get-error-message'
|
||||
import { saveUserMappingRule } from '@/lib/bookkeeping/mapping-engine'
|
||||
import { upsertCounterpartyTemplate, buildMappingResultFromCounterpartyTemplate } from '@/lib/bookkeeping/counterparty-templates'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
@@ -297,7 +299,15 @@ export async function POST(
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to create journal entry:', err)
|
||||
journalEntryError = err instanceof Error ? err.message : 'Unknown error'
|
||||
// Typed bookkeeping errors: surface a Swedish translation in the response
|
||||
// so the client toast can display it directly.
|
||||
const typedResp = bookkeepingErrorResponse(err)
|
||||
if (typedResp) {
|
||||
const body = (await typedResp.json()) as unknown
|
||||
journalEntryError = getErrorMessage(body, { context: 'transaction' })
|
||||
} else {
|
||||
journalEntryError = err instanceof Error ? err.message : 'Unknown error'
|
||||
}
|
||||
// Continue - we still want to save the categorization
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,12 @@ import {
|
||||
createInvoiceCashEntry,
|
||||
} from '@/lib/bookkeeping/invoice-entries'
|
||||
import { reverseEntry } from '@/lib/bookkeeping/engine'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import {
|
||||
AccountsNotInChartError,
|
||||
accountsNotInChartResponse,
|
||||
bookkeepingErrorResponse,
|
||||
} from '@/lib/bookkeeping/errors'
|
||||
import { getErrorMessage } from '@/lib/errors/get-error-message'
|
||||
import { validateBody } from '@/lib/api/validate'
|
||||
import { MatchInvoiceSchema } from '@/lib/api/schemas'
|
||||
import { logMatchEvent } from '@/lib/invoices/match-log'
|
||||
@@ -122,9 +127,8 @@ export async function POST(
|
||||
newState: { journal_entry_id: null },
|
||||
})
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
console.error('Failed to storno conflicting journal entry:', err)
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to reverse conflicting journal entry' },
|
||||
@@ -204,11 +208,21 @@ export async function POST(
|
||||
journalEntryId = journalEntry?.id ?? null
|
||||
}
|
||||
} catch (err) {
|
||||
// AccountsNotInChart returns the structured 400 so the UI can open the
|
||||
// account-activation dialog. Other errors are logged and attached to
|
||||
// `journal_entry_error` — the match itself is a valuable business event,
|
||||
// and the user can re-book the payment verifikation separately.
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
console.error('Failed to create payment journal entry:', err)
|
||||
journalEntryError = err instanceof Error ? err.message : 'Unknown error'
|
||||
const typedResp = bookkeepingErrorResponse(err)
|
||||
if (typedResp) {
|
||||
const body = (await typedResp.json()) as unknown
|
||||
journalEntryError = getErrorMessage(body, { context: 'invoice' })
|
||||
} else {
|
||||
journalEntryError = err instanceof Error ? err.message : 'Unknown error'
|
||||
}
|
||||
// Continue - we still want to update the invoice and transaction
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createSupplierInvoicePaymentEntry,
|
||||
createSupplierInvoiceCashEntry,
|
||||
} from '@/lib/bookkeeping/supplier-invoice-entries'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { validateBody } from '@/lib/api/validate'
|
||||
import { MatchSupplierInvoiceSchema } from '@/lib/api/schemas'
|
||||
import { logMatchEvent } from '@/lib/invoices/match-log'
|
||||
@@ -128,9 +128,8 @@ export async function POST(
|
||||
if (journalEntry) journalEntryId = journalEntry.id
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(err)
|
||||
}
|
||||
const typed = bookkeepingErrorResponse(err)
|
||||
if (typed) return typed
|
||||
console.error('Failed to create payment journal entry:', err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { reverseEntry } from '@/lib/bookkeeping/engine'
|
||||
import { AccountsNotInChartError, accountsNotInChartResponse } from '@/lib/bookkeeping/errors'
|
||||
import { bookkeepingErrorResponse } from '@/lib/bookkeeping/errors'
|
||||
import { ensureInitialized } from '@/lib/init'
|
||||
import { requireCompanyId } from '@/lib/company/context'
|
||||
import { requireWritePermission } from '@/lib/auth/require-write'
|
||||
@@ -61,9 +61,8 @@ export async function POST(
|
||||
try {
|
||||
await reverseEntry(supabase, companyId, user.id, transaction.journal_entry_id)
|
||||
} catch (err) {
|
||||
if (err instanceof AccountsNotInChartError) {
|
||||
return accountsNotInChartResponse(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 })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user