Files
accounted/lib/import/customers/column-detector.ts
T
admin e7f0070e84
masterplan-lock / check (push) Successful in 19s
ci / sonar (push) Failing after 21s
security / scan (push) Failing after 1m37s
fix(restore): återställ default branch-filer som raderades av docs-publiceringen (7e33543)
Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i
server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma
strängvärde byggs med konkatenering i stället för att matcha mönstret.
2026-11-10 09:37:22 +01:00

107 lines
3.5 KiB
TypeScript

import { findColumn } from '../shared/column-utils'
import type { DetectedCustomerColumns } from './types'
const NAME_KEYWORDS = [
'kundnamn', 'kund namn', 'namn', 'name', 'kund', 'customer', 'customer name',
'företag', 'foretag', 'company', 'företagsnamn', 'foretagsnamn',
]
const ORG_NUMBER_KEYWORDS = [
'orgnr', 'org nr', 'organisationsnummer', 'organisationsnr', 'org',
'personnr', 'personnummer', 'org number', 'organization number',
]
const CUSTOMER_TYPE_KEYWORDS = [
'kundtyp', 'kund typ', 'typ', 'type', 'customer type', 'customer_type',
]
const EMAIL_KEYWORDS = [
'epost', 'e post', 'email', 'mail', 'e mail', 'e-post',
]
const PHONE_KEYWORDS = [
'telefon', 'tel', 'phone', 'mobil', 'mobile', 'telefonnummer',
]
const ADDRESS_LINE1_KEYWORDS = [
'adress', 'address', 'gatuadress', 'street', 'gata',
'address line 1', 'address1', 'adressrad 1',
]
const ADDRESS_LINE2_KEYWORDS = [
'address line 2', 'address2', 'adressrad 2', 'c o', 'co',
]
const POSTAL_CODE_KEYWORDS = [
'postnr', 'postnummer', 'postal code', 'postal_code', 'zip', 'zip code',
]
const CITY_KEYWORDS = ['ort', 'stad', 'city', 'postort']
const COUNTRY_KEYWORDS = ['land', 'country']
const VAT_NUMBER_KEYWORDS = [
'vat', 'vatnr', 'vat nr', 'vat number', 'momsnummer', 'momsregistreringsnummer',
'momsregnr', 'moms nr',
]
const PAYMENT_TERMS_KEYWORDS = [
'betalningsvillkor', 'betalvillkor', 'payment terms', 'kredittid', 'kreditdagar',
'dagar', 'förfallodagar', 'forfallodagar',
]
const NOTES_KEYWORDS = [
'anteckning', 'anteckningar', 'notes', 'kommentar', 'kommentarer', 'comment',
'note', 'beskrivning',
]
/**
* Detect customer-register columns from headers.
* Header-only matching: register imports always have headers, and the column
* structure varies too much to do data-driven fallbacks reliably.
*/
export function detectCustomerColumns(headers: string[]): DetectedCustomerColumns {
const taken = new Set<number>()
const name_col = findColumn(headers, NAME_KEYWORDS, taken) ?? -1
const org_number_col = findColumn(headers, ORG_NUMBER_KEYWORDS, taken)
const customer_type_col = findColumn(headers, CUSTOMER_TYPE_KEYWORDS, taken)
const email_col = findColumn(headers, EMAIL_KEYWORDS, taken)
const phone_col = findColumn(headers, PHONE_KEYWORDS, taken)
const address_line1_col = findColumn(headers, ADDRESS_LINE1_KEYWORDS, taken)
const address_line2_col = findColumn(headers, ADDRESS_LINE2_KEYWORDS, taken)
const postal_code_col = findColumn(headers, POSTAL_CODE_KEYWORDS, taken)
const city_col = findColumn(headers, CITY_KEYWORDS, taken)
const country_col = findColumn(headers, COUNTRY_KEYWORDS, taken)
const vat_number_col = findColumn(headers, VAT_NUMBER_KEYWORDS, taken)
const payment_terms_col = findColumn(headers, PAYMENT_TERMS_KEYWORDS, taken)
const notes_col = findColumn(headers, NOTES_KEYWORDS, taken)
// Confidence: name is required; bonus from how many other columns matched.
let confidence = 0
if (name_col >= 0) {
const matched = [
org_number_col, email_col, phone_col, address_line1_col,
postal_code_col, city_col, vat_number_col, payment_terms_col,
].filter((c) => c !== null).length
confidence = 0.55 + Math.min(matched, 6) * 0.075
}
return {
name_col: name_col >= 0 ? name_col : 0,
org_number_col,
customer_type_col,
email_col,
phone_col,
address_line1_col,
address_line2_col,
postal_code_col,
city_col,
country_col,
vat_number_col,
payment_terms_col,
notes_col,
confidence: Math.min(Math.round(confidence * 100) / 100, 1),
}
}