fix(vat): complete account treatment enforcement (#1593)

* fix(vat): complete account treatment enforcement

* docs(api): refresh account endpoint skill

* fix(mcp): preserve ruta 05 compatibility

* test(vat): seed migration constraint fixtures

* docs(vat): clarify treatment precedence

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-08-15 23:45:04 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent edfdbe2d2a
commit 86f0b70fdd
43 changed files with 1017 additions and 359 deletions
+3 -17
View File
@@ -1996,23 +1996,6 @@ describe('CreateAccountSchema', () => {
})
expect(result.success).toBe(false)
})
it('rejects a VAT treatment that does not apply to the account class', () => {
expect(CreateAccountSchema.safeParse({
account_number: '5010',
account_name: 'Consulting costs',
account_type: 'expense',
normal_balance: 'debit',
default_vat_treatment: 'standard_25',
}).success).toBe(false)
expect(CreateAccountSchema.safeParse({
account_number: '5010',
account_name: 'EU services',
account_type: 'expense',
normal_balance: 'debit',
default_vat_treatment: 'reverse_charge_eu_services',
}).success).toBe(true)
})
})
// ============================================================
@@ -2327,6 +2310,9 @@ describe('UpdateAccountSchema', () => {
expect(UpdateAccountSchema.safeParse({
default_vat_treatment: 'reverse_charge_eu_goods',
}).success).toBe(true)
expect(UpdateAccountSchema.safeParse({
default_vat_treatment: 'reverse_charge_non_eu_services',
}).success).toBe(true)
expect(UpdateAccountSchema.safeParse({ default_vat_treatment: null }).success).toBe(true)
expect(UpdateAccountSchema.safeParse({ default_vat_treatment: 'eu_purchase' }).success).toBe(false)
})
+2 -17
View File
@@ -16,7 +16,6 @@ import { INVOICE_POSTING_ACCOUNT_REGEX } from '@/lib/invoices/posting-account'
import { PERSONAL_NUMBER_INPUT_RE } from '@/lib/customers/mask-personal-number'
import type { AuditAction } from '@/types'
import type { BankFileFormatId } from '@/lib/import/bank-file/types'
import { isVatTreatmentValidForAccountClass } from '@/lib/vat/account-vat-treatment'
// ============================================================
// Shared primitives
@@ -2124,8 +2123,8 @@ const defaultVatRate = z
export const AccountVatTreatmentSchema = z.enum([
'standard_25', 'reduced_12', 'reduced_6', 'exempt',
'reverse_charge_domestic', 'reverse_charge_eu_goods',
'reverse_charge_eu_services', 'export_goods', 'export_services',
'vmb', 'rental_voluntary',
'reverse_charge_eu_services', 'reverse_charge_non_eu_services',
'export_goods', 'export_services', 'vmb', 'rental_voluntary',
])
const defaultVatTreatment = AccountVatTreatmentSchema.nullable().optional()
@@ -2141,20 +2140,6 @@ export const CreateAccountSchema = z.object({
default_vat_rate: defaultVatRate,
default_vat_treatment: defaultVatTreatment,
sru_code: z.string().nullable().optional(),
}).superRefine((value, ctx) => {
if (
value.default_vat_treatment &&
!isVatTreatmentValidForAccountClass(
value.default_vat_treatment,
Number(value.account_number.charAt(0)),
)
) {
ctx.addIssue({
code: 'custom',
path: ['default_vat_treatment'],
message: 'VAT treatment is not valid for the account class',
})
}
})
export const UpdateAccountSchema = z.object({