feat: add safe owner-only migration reset (#1682)
* feat: add safe company migration reset * fix: harden company reset eligibility * fix: close company reset compliance gaps * test: fix migration reset pg-real probes * fix: preserve migration archive access * docs: explain migration numbering continuity * fix: block reset with VAT workflow state * fix: block externally staged reset data * fix: address migration reset review findings * fix: clear stale migration archive estimate * fix: retry migration archive estimates
This commit is contained in:
@@ -3640,3 +3640,32 @@ export const SkattekontoFileExecuteSchema = z.object({
|
||||
variant: z.enum(['csv', 'skv']),
|
||||
closing_saldo: z.number().finite().nullable().optional(),
|
||||
})
|
||||
|
||||
// ============================================================
|
||||
// Company migration reset
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* POST /api/company/[id]/migration-reset
|
||||
*
|
||||
* Every confirmation is required independently. The database repeats these
|
||||
* checks inside the atomic RPC, including the exact display-name match.
|
||||
*/
|
||||
export const CompanyMigrationResetSchema = z.object({
|
||||
confirm_name: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, 'Ange företagsnamnet exakt som det visas')
|
||||
.max(200, 'Företagsnamnet får vara högst 200 tecken'),
|
||||
reason: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(20, 'Beskriv varför migreringen behöver göras om med minst 20 tecken')
|
||||
.max(1000, 'Beskrivningen får vara högst 1 000 tecken'),
|
||||
confirm_no_filed_declarations: z.literal(true, {
|
||||
error: 'Bekräfta att inga deklarationer eller årsredovisningar har lämnats in',
|
||||
}),
|
||||
confirm_retained_archive: z.literal(true, {
|
||||
error: 'Bekräfta att den tidigare företagskopian sparas som arkiv',
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -2698,6 +2698,41 @@ const COMPANY: Record<string, StructuredErrorEntry> = {
|
||||
message_sv: 'Företaget kunde inte skapas.',
|
||||
message_en: 'Failed to create company.',
|
||||
},
|
||||
COMPANY_RESET_NOT_FOUND: {
|
||||
httpStatus: 404,
|
||||
message_sv: 'Företaget kunde inte hittas.',
|
||||
message_en: 'Company not found.',
|
||||
},
|
||||
COMPANY_RESET_FORBIDDEN: {
|
||||
httpStatus: 403,
|
||||
message_sv: 'Endast företagets ägare kan starta om en migrering.',
|
||||
message_en: 'Only the company owner can reset a migration.',
|
||||
},
|
||||
COMPANY_RESET_INELIGIBLE: {
|
||||
httpStatus: 409,
|
||||
message_sv: 'Företaget kan inte återställas med självservice. Kontakta supporten för en individuell bedömning.',
|
||||
message_en: 'The company is not eligible for a self-service reset. Contact support for an individual review.',
|
||||
},
|
||||
COMPANY_RESET_CONFIRMATION_MISMATCH: {
|
||||
httpStatus: 400,
|
||||
message_sv: 'Företagsnamnet stämmer inte överens.',
|
||||
message_en: 'The company name does not match.',
|
||||
},
|
||||
COMPANY_RESET_REASON_INVALID: {
|
||||
httpStatus: 400,
|
||||
message_sv: 'Beskriv varför migreringen behöver göras om med 20 till 1 000 tecken.',
|
||||
message_en: 'Explain why the migration must be redone using 20 to 1,000 characters.',
|
||||
},
|
||||
COMPANY_RESET_CONFIRMATION_REQUIRED: {
|
||||
httpStatus: 400,
|
||||
message_sv: 'Alla säkerhetsbekräftelser krävs.',
|
||||
message_en: 'All safety confirmations are required.',
|
||||
},
|
||||
COMPANY_RESET_FAILED: {
|
||||
httpStatus: 500,
|
||||
message_sv: 'Migreringen kunde inte startas om. Inga ändringar har sparats.',
|
||||
message_en: 'The migration reset failed. No changes were saved.',
|
||||
},
|
||||
}
|
||||
|
||||
const API_KEY: Record<string, StructuredErrorEntry> = {
|
||||
|
||||
Reference in New Issue
Block a user