Fix/bank and onboarding (#413)
* fix(onboarding): validate org number at step 2 instead of final submit Run normalizeOrgNumber (Luhn + 10/12-digit check) inside the Step 2 Zod schema and gate the duplicate-check and TIC lookup effects on it, so users get an inline error on the field they just typed instead of filling out two more steps and being bounced back from the server. Server-side check stays as defense in depth. Also fixes the old regex incorrectly rejecting valid 12-digit org numbers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(reconciliation): pass real userId to transaction.reconciled events runReconciliation and manualLink fell back to companyId when no userId was provided, causing FK violations on event_log.user_id (which references auth.users). Make userId a required arg in both functions, drop the fallback, and forward a real user id from every caller (user.id from authed routes, connection.user_id from the cron path). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(enable-banking): use local companyId instead of reinlining ctx fallback Three sites in the enable-banking sync handler reinline `ctx?.companyId ?? user.id` instead of using the local `companyId` declared at the top of the block. Collapse all three to the local for consistency and to remove drift risk if the fallback expression ever changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
dbe634d0aa
commit
9e1e13d388
@@ -12,6 +12,7 @@ import { Loader2, ArrowRight, ArrowLeft, CheckCircle2, AlertTriangle } from 'luc
|
||||
import type { EntityType } from '@/types'
|
||||
import type { CompanyLookupResult } from '@/lib/company-lookup/types'
|
||||
import { getBranding } from '@/lib/branding/service'
|
||||
import { normalizeOrgNumber } from '@/lib/company-lookup/normalize-org-number'
|
||||
|
||||
const branding = getBranding()
|
||||
|
||||
@@ -19,7 +20,10 @@ const schema = z.object({
|
||||
company_name: z.string().min(1, 'Företagsnamn krävs'),
|
||||
org_number: z.string()
|
||||
.min(1, 'Organisationsnummer krävs')
|
||||
.regex(/^\d{6,8}[-\s]?\d{4}$/, 'Ogiltigt format. Ange XXXXXX-XXXX'),
|
||||
.refine(
|
||||
(val) => normalizeOrgNumber(val) !== null,
|
||||
'Ogiltigt organisationsnummer. Kontrollera att du angett ett giltigt 10- eller 12-siffrigt organisationsnummer.',
|
||||
),
|
||||
address_line1: z.string().optional(),
|
||||
postal_code: z.string().optional(),
|
||||
city: z.string().optional(),
|
||||
@@ -27,8 +31,6 @@ const schema = z.object({
|
||||
|
||||
type FormData = z.infer<typeof schema>
|
||||
|
||||
const ORG_NUMBER_REGEX = /^\d{6,8}[-\s]?\d{4}$/
|
||||
|
||||
interface Step2Props {
|
||||
initialData: Partial<FormData>
|
||||
entityType?: EntityType
|
||||
@@ -82,7 +84,7 @@ export default function Step2CompanyDetails({
|
||||
// button is disabled; the server action would also reject ('org_number_exists')
|
||||
// but blocking client-side avoids a wasted roundtrip.
|
||||
useEffect(() => {
|
||||
if (!orgNumber || !ORG_NUMBER_REGEX.test(orgNumber)) {
|
||||
if (!orgNumber || normalizeOrgNumber(orgNumber) === null) {
|
||||
setOrgNumberExists(false)
|
||||
return
|
||||
}
|
||||
@@ -109,7 +111,7 @@ export default function Step2CompanyDetails({
|
||||
}, [orgNumber])
|
||||
|
||||
useEffect(() => {
|
||||
if (!ticEnabled || !orgNumber || !ORG_NUMBER_REGEX.test(orgNumber)) {
|
||||
if (!ticEnabled || !orgNumber || normalizeOrgNumber(orgNumber) === null) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user