feat: allow replacing completed SIE imports (#227)

* feat: allow replacing completed SIE imports

Users who import a SIE file, make adjustments in the source system, and
re-export can now replace the old import instead of being permanently
blocked by the "overlapping fiscal year" guard.

The old import's entries are cancelled (posted → cancelled) and the
import is marked as 'replaced'. Nothing is deleted — full audit trail
preserved per BFL 5 kap 5§ (rättelse) and BFNAR 2013:2 kap 8.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review — atomic RPC, locked_at check

- P1: Wrap entry cancellation + import status update in a single DB RPC
  (replace_sie_import) to prevent inconsistent state on partial failure
- P2: Check locked_at in addition to is_closed for fiscal period guard
- P2: Use RPC return value for accurate cancelled entry count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-04-13 10:48:49 +02:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 973a2b81a0
commit 258a64a849
6 changed files with 250 additions and 6 deletions
+42
View File
@@ -0,0 +1,42 @@
import { createClient } from '@/lib/supabase/server'
import { NextResponse } from 'next/server'
import { requireCompanyId } from '@/lib/company/context'
import { requireWritePermission } from '@/lib/auth/require-write'
import { replaceSIEImport } from '@/lib/import/sie-import'
/**
* POST /api/import/sie/[id]/replace
* Replace a completed SIE import by cancelling its entries, allowing
* the user to re-import corrected data for the same fiscal period.
*/
export async function POST(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const supabase = await createClient()
const { id } = await params
const {
data: { user },
} = await supabase.auth.getUser()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const writeCheck = await requireWritePermission(supabase, user.id)
if (!writeCheck.ok) return writeCheck.response
const companyId = await requireCompanyId(supabase, user.id)
const result = await replaceSIEImport(supabase, companyId, id)
if (!result.success) {
return NextResponse.json({ error: result.error }, { status: 400 })
}
return NextResponse.json({
success: true,
cancelledEntries: result.cancelledEntries,
})
}