Files
accounted/lib/extensions/validation.ts
T
Emil 6a5b2b7792 feat: overhaul all 12 sector extensions with full CRUD, validation, and tests
Add shared components (ConfirmDeleteDialog, EditEntryDialog, validation utils),
enhance all 12 extension workspaces with edit/delete dialogs, input validation,
period comparisons, and new analytics features. Fix critical bugs in
ProjectBilling margin calculation and EarningsPerLiter revenue allocation.
Add pure calculation modules with 183 new tests across all extensions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 15:21:49 +01:00

71 lines
2.3 KiB
TypeScript

/**
* Validates a Swedish personal number (YYYYMMDD-XXXX) using Luhn checksum.
* Returns an error message string, or null if valid.
*/
export function validateSwedishPersonalNumber(pnr: string): string | null {
if (!pnr) return 'Personnummer kravs'
// Accept YYYYMMDD-XXXX or YYYYMMDDXXXX
const cleaned = pnr.replace(/[-\s]/g, '')
if (!/^\d{12}$/.test(cleaned)) {
return 'Format: YYYYMMDD-XXXX (12 siffror)'
}
const year = parseInt(cleaned.slice(0, 4))
const month = parseInt(cleaned.slice(4, 6))
const day = parseInt(cleaned.slice(6, 8))
if (month < 1 || month > 12) return 'Ogiltig manad'
if (day < 1 || day > 31) return 'Ogiltig dag'
if (year < 1900 || year > new Date().getFullYear()) return 'Ogiltigt ar'
// Luhn check on the last 10 digits (YYMMDDXXXX)
const luhnDigits = cleaned.slice(2)
let sum = 0
for (let i = 0; i < 10; i++) {
let digit = parseInt(luhnDigits[i])
if (i % 2 === 0) {
digit *= 2
if (digit > 9) digit -= 9
}
sum += digit
}
if (sum % 10 !== 0) return 'Ogiltig kontrollsiffra'
return null
}
export function validatePositiveNumber(value: number | string): string | null {
const num = typeof value === 'string' ? parseFloat(value) : value
if (isNaN(num) || num <= 0) return 'Varde maste vara storre an 0'
return null
}
export function validateNonNegativeNumber(value: number | string): string | null {
const num = typeof value === 'string' ? parseFloat(value) : value
if (isNaN(num) || num < 0) return 'Varde kan inte vara negativt'
return null
}
export function validateMaxNumber(value: number | string, max: number): string | null {
const num = typeof value === 'string' ? parseFloat(value) : value
if (isNaN(num)) return 'Ogiltigt varde'
if (num > max) return `Varde kan inte overskrida ${max}`
return null
}
export function validateRequired(value: string | number | undefined | null): string | null {
if (value === undefined || value === null || value === '') return 'Obligatoriskt falt'
return null
}
export function validateDateNotFuture(dateStr: string): string | null {
if (!dateStr) return 'Datum kravs'
const date = new Date(dateStr)
const today = new Date()
today.setHours(23, 59, 59, 999)
if (date > today) return 'Datum kan inte vara i framtiden'
return null
}