@@ -4,6 +4,7 @@ import { validateBody } from '@/lib/api/validate'
|
||||
import { sparsePatchBody } from '@/lib/api/sparse-patch'
|
||||
import { UpdateAccountSchema } from '@/lib/api/schemas'
|
||||
import { getErrorMessage as getUserErrorMessage } from '@/lib/errors/get-error-message'
|
||||
import { isVatTreatmentValidForAccountClass } from '@/lib/vat/account-vat-treatment'
|
||||
|
||||
// DELETE hard-deletes an unused, non-system account; accounts referenced by
|
||||
// this company's journal entries must be deactivated instead (PUT is_active).
|
||||
@@ -95,7 +96,7 @@ export const PUT = withRouteContext(
|
||||
// .default() today, so sparsePatchBody is a no-op here: it is the
|
||||
// structural guarantee that adding one later cannot make a PUT that
|
||||
// renames an account also rewrite its VAT code or SRU mapping. An
|
||||
// explicit null (clearing sru_code, default_vat_code, default_vat_rate)
|
||||
// explicit null (clearing sru_code, VAT defaults, or descriptions)
|
||||
// still survives.
|
||||
const validation = await validateBody(request, sparsePatchBody(UpdateAccountSchema), {
|
||||
log,
|
||||
@@ -104,6 +105,19 @@ export const PUT = withRouteContext(
|
||||
if (!validation.success) return validation.response
|
||||
const body = validation.data
|
||||
|
||||
if (
|
||||
body.default_vat_treatment &&
|
||||
!isVatTreatmentValidForAccountClass(
|
||||
body.default_vat_treatment,
|
||||
Number(number.charAt(0)),
|
||||
)
|
||||
) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Momshanteringen är inte giltig för kontoklassen' },
|
||||
{ status: 400 },
|
||||
)
|
||||
}
|
||||
|
||||
if (Object.keys(body).length === 0) {
|
||||
return NextResponse.json({ error: 'Inget att uppdatera' }, { status: 400 })
|
||||
}
|
||||
|
||||
@@ -267,6 +267,28 @@ describe('POST /api/bookkeeping/accounts', () => {
|
||||
}
|
||||
expect(insertArg?.default_vat_rate).toBe(0)
|
||||
})
|
||||
|
||||
it('forwards default_vat_treatment into the insert', async () => {
|
||||
const { supabase, calls } = createCapturingSupabase([{
|
||||
data: { account_number: '4056', default_vat_treatment: 'reverse_charge_eu_goods' },
|
||||
}])
|
||||
auth(supabase)
|
||||
const req = createMockRequest('/api/bookkeeping/accounts', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
account_number: '4056',
|
||||
account_name: 'Inköp varor EU',
|
||||
account_type: 'expense',
|
||||
normal_balance: 'debit',
|
||||
default_vat_treatment: 'reverse_charge_eu_goods',
|
||||
},
|
||||
})
|
||||
expect((await createPOST(req, routeParams)).status).toBe(200)
|
||||
const insertArg = calls.find((c) => c.method === 'insert')?.args[0] as {
|
||||
default_vat_treatment?: string | null
|
||||
}
|
||||
expect(insertArg.default_vat_treatment).toBe('reverse_charge_eu_goods')
|
||||
})
|
||||
})
|
||||
|
||||
describe('DELETE /api/bookkeeping/accounts/[number]', () => {
|
||||
@@ -376,6 +398,33 @@ describe('PUT /api/bookkeeping/accounts/[number]', () => {
|
||||
expect(updateArg?.default_vat_rate).toBe(0)
|
||||
})
|
||||
|
||||
it('forwards default_vat_treatment into the update', async () => {
|
||||
const { supabase, calls } = createCapturingSupabase([{
|
||||
data: { account_number: '3041', default_vat_treatment: 'standard_25' },
|
||||
}])
|
||||
auth(supabase)
|
||||
const req = createMockRequest('/api/bookkeeping/accounts/3041', {
|
||||
method: 'PUT',
|
||||
body: { default_vat_treatment: 'standard_25' },
|
||||
})
|
||||
expect((await PUT(req, { params: Promise.resolve({ number: '3041' }) })).status).toBe(200)
|
||||
const updateArg = calls.find((c) => c.method === 'update')?.args[0] as {
|
||||
default_vat_treatment?: string | null
|
||||
}
|
||||
expect(updateArg.default_vat_treatment).toBe('standard_25')
|
||||
})
|
||||
|
||||
it('rejects a VAT treatment that does not apply to the account class', async () => {
|
||||
const { supabase, calls } = createCapturingSupabase([])
|
||||
auth(supabase)
|
||||
const req = createMockRequest('/api/bookkeeping/accounts/5010', {
|
||||
method: 'PUT',
|
||||
body: { default_vat_treatment: 'standard_25' },
|
||||
})
|
||||
expect((await PUT(req, numberParams)).status).toBe(400)
|
||||
expect(calls.some((call) => call.method === 'update')).toBe(false)
|
||||
})
|
||||
|
||||
// The body is spread straight into .update(), so the write set must be
|
||||
// exactly what the caller named. UpdateAccountSchema carries no .default()
|
||||
// today; these two lock the property in so adding one cannot turn a rename
|
||||
|
||||
@@ -104,6 +104,7 @@ export const POST = withRouteContext(
|
||||
description: body.description || null,
|
||||
default_vat_code: body.default_vat_code || null,
|
||||
default_vat_rate: body.default_vat_rate ?? null,
|
||||
default_vat_treatment: body.default_vat_treatment ?? null,
|
||||
sru_code: body.sru_code || null,
|
||||
sort_order: parseInt(body.account_number),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user