Kunden: Reiter "Abgesagt", Absage eintragen und Warnung vor Doppelten
- Kundenliste mit Reiter "Abgesagt" (vorher nur unter "Alle"), rotes Abzeichen, Suche auch nach Telefon - Kundenseite: "Absage eintragen" fuer Leads (Grund freiwillig); Vermerk "Abgesagt am TT.MM.JJJJ (Name): Grund" oben in die Notizen, offene Akquise-Tickets auf "Abgesagt"; roter Hinweis "nicht erneut anrufen" und "Wieder als Lead fuehren" - Neuer Lead (jetzt mit Telefon) und Neuer Kunde gleichen live mit allen Kunden ab (auch archivierte/abgesagte): Name ohne Rechtsform, E-Mail, Firmen-Domain (ohne Freemailer), Telefon; Abgesagte rot und zuerst, Knopf heisst dann "Trotzdem anlegen" - Uebersicht wird nach Speichern neu aufgebaut (key), damit kein alter Notizstand den Vermerk ueberschreibt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5d8130e8ef
commit
d2883018fc
10 files changed
+378
-23
No files matched your search
@@ -16,7 +16,7 @@ const ids = (list) => list.map((c) => c.$id)
|
||||
|
||||
test('CUSTOMER_TABS, customerStage, isArchived', () => {
|
||||
assert.deepEqual(CUSTOMER_TABS, [
|
||||
{ id: 'customer', label: 'Feste Kunden' }, { id: 'lead', label: 'Leads' },
|
||||
{ id: 'customer', label: 'Feste Kunden' }, { id: 'lead', label: 'Leads' }, { id: 'lost', label: 'Abgesagt' },
|
||||
{ id: 'archived', label: 'Archiviert' }, { id: 'all', label: 'Alle' },
|
||||
])
|
||||
assert.equal(customerStage({ customerStatus: 'LEAD' }), 'lead')
|
||||
@@ -29,6 +29,8 @@ test('CUSTOMER_TABS, customerStage, isArchived', () => {
|
||||
test('filterCustomers: jeder Reiter', () => {
|
||||
assert.deepEqual(ids(filterCustomers(ALL, { stage: 'customer' })), ['a', 'd'])
|
||||
assert.deepEqual(ids(filterCustomers(ALL, { stage: 'lead' })), ['b'])
|
||||
assert.deepEqual(ids(filterCustomers(ALL, { stage: 'lost' })), ['c'])
|
||||
assert.deepEqual(ids(filterCustomers([{ $id: 'x', customerStatus: 'lost', archived: true }], { stage: 'lost' })), [])
|
||||
assert.deepEqual(ids(filterCustomers(ALL, { stage: 'archived' })), ['e', 'f'])
|
||||
assert.deepEqual(ids(filterCustomers(ALL, { stage: 'all' })), ['a', 'b', 'c', 'd'])
|
||||
assert.deepEqual(ids(filterCustomers(ALL)), ['a', 'd'])
|
||||
@@ -36,19 +38,20 @@ test('filterCustomers: jeder Reiter', () => {
|
||||
assert.deepEqual(filterCustomers(undefined, { stage: 'all' }), [])
|
||||
})
|
||||
|
||||
test('filterCustomers: Suche in Name, Code, Standort, E-Mail und Ansprechpartner', () => {
|
||||
test('filterCustomers: Suche in Name, Code, Standort, E-Mail, Ansprechpartner und Telefon', () => {
|
||||
const find = (search, stage = 'all') => ids(filterCustomers(ALL, { stage, search }))
|
||||
for (const s of ['ALPHA', 'k-1', 'kaisers', 'info@alpha', 'anna']) assert.deepEqual(find(s), ['a'], s)
|
||||
assert.deepEqual(find('xyz'), [])
|
||||
assert.deepEqual(ids(filterCustomers([{ $id: 'p', phone: '0631 12345' }], { stage: 'all', search: '0631 123' })), ['p'])
|
||||
assert.deepEqual(find('eta', 'lead'), ['b'])
|
||||
assert.deepEqual(find(''), ['a', 'b', 'c', 'd'])
|
||||
assert.deepEqual(ids(filterCustomers([{ $id: 'n' }], { stage: 'all', search: 'undefined' })), [])
|
||||
})
|
||||
|
||||
test('stageCounts: dieselbe Filterfunktion wie die Liste', () => {
|
||||
assert.deepEqual(stageCounts(ALL), { customer: 2, lead: 1, archived: 2, all: 4 })
|
||||
assert.deepEqual(stageCounts(ALL, 'eta'), { customer: 0, lead: 1, archived: 1, all: 1 })
|
||||
assert.deepEqual(stageCounts([]), { customer: 0, lead: 0, archived: 0, all: 0 })
|
||||
assert.deepEqual(stageCounts(ALL), { customer: 2, lead: 1, lost: 1, archived: 2, all: 4 })
|
||||
assert.deepEqual(stageCounts(ALL, 'eta'), { customer: 0, lead: 1, lost: 0, archived: 1, all: 1 })
|
||||
assert.deepEqual(stageCounts([]), { customer: 0, lead: 0, lost: 0, archived: 0, all: 0 })
|
||||
})
|
||||
|
||||
test('customerBadges: Reihenfolge, Töne, Option stage', () => {
|
||||
@@ -57,7 +60,7 @@ test('customerBadges: Reihenfolge, Töne, Option stage', () => {
|
||||
{ key: 'lead', label: 'Lead', tone: 'muted' },
|
||||
{ key: 'archived', label: 'Archiviert', tone: 'plain' },
|
||||
])
|
||||
assert.deepEqual(customerBadges(C), [{ key: 'lost', label: 'Abgesagt', tone: 'plain' }])
|
||||
assert.deepEqual(customerBadges(C), [{ key: 'lost', label: 'Abgesagt', tone: 'danger' }])
|
||||
for (const c of [A, D, null]) assert.deepEqual(customerBadges(c), [])
|
||||
assert.deepEqual(customerBadges(E, { stage: false }).map((b) => b.key), ['important', 'archived'])
|
||||
})
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { loadModule } from './load.mjs'
|
||||
|
||||
const { normalizeName, normalizePhone, companyDomain, findDuplicates, rejectionLine, notesWithRejection, lastRejection } =
|
||||
await loadModule('src/lib/leadDuplicates.js')
|
||||
|
||||
test('normalizeName: Rechtsform, Umlaute, Satzzeichen, einzelne Buchstaben', () => {
|
||||
assert.equal(normalizeName('Bäckerei Müller GmbH & Co. KG'), 'baeckerei mueller')
|
||||
assert.equal(normalizeName('Café Rosé'), 'cafe rose')
|
||||
assert.equal(normalizeName('Friseur Amin e.K.'), 'friseur amin')
|
||||
assert.equal(normalizeName('Straße-Bau UG (haftungsbeschränkt)'), 'strasse bau')
|
||||
assert.equal(normalizeName(''), '')
|
||||
assert.equal(normalizeName(undefined), '')
|
||||
})
|
||||
|
||||
test('normalizePhone: Ziffern, +49 und 0049 als 0, zu kurz zählt nicht', () => {
|
||||
assert.equal(normalizePhone('+49 631 / 123 45-6'), '0631123456')
|
||||
assert.equal(normalizePhone('0049 631 1234560'), '06311234560')
|
||||
assert.equal(normalizePhone('0631 1234560'), '06311234560')
|
||||
assert.equal(normalizePhone('12 34'), '')
|
||||
assert.equal(normalizePhone(null), '')
|
||||
})
|
||||
|
||||
test('companyDomain: Freemailer zählen nicht', () => {
|
||||
assert.equal(companyDomain('Info@Baecker-Mueller.de'), 'baecker-mueller.de')
|
||||
assert.equal(companyDomain('x@gmail.com'), '')
|
||||
assert.equal(companyDomain('x@web.de'), '')
|
||||
assert.equal(companyDomain('kaputt'), '')
|
||||
})
|
||||
|
||||
const LIST = [
|
||||
{ $id: 'a', name: 'Bäckerei Müller GmbH', email: 'info@mueller-backt.de', phone: '0631 123456', customerStatus: 'lead' },
|
||||
{ $id: 'b', name: 'Friseur Amin', email: 'amin@gmail.com', customerStatus: 'customer' },
|
||||
{ $id: 'c', name: 'Autohaus Schmidt Kaiserslautern', phone: '+49 6301 9999', customerStatus: 'lost', notes: 'Abgesagt am 01.10.2026 (Kenso): kein Bedarf\nalt' },
|
||||
{ $id: 'd', name: 'Bäckerei', customerStatus: 'lead', archived: true },
|
||||
]
|
||||
const hit = (input, opts) => findDuplicates(LIST, input, opts).map((m) => `${m.customer.$id}:${m.reasons.join('+')}`)
|
||||
|
||||
test('findDuplicates: Name, E-Mail, Domain, Telefon; Abgesagte zuerst', () => {
|
||||
assert.deepEqual(hit({ name: 'Baeckerei Mueller' }), ['a:Name']) // ae/ue wie ä/ü
|
||||
assert.deepEqual(hit({ name: 'bäckerei müller' }), ['a:Name'])
|
||||
assert.deepEqual(hit({ name: 'Bäckerei' }), ['d:Name']) // archiviert zählt mit; kurzer Name nur exakt
|
||||
assert.deepEqual(hit({ email: 'INFO@mueller-backt.de' }), ['a:E-Mail'])
|
||||
assert.deepEqual(hit({ email: 'chef@mueller-backt.de' }), ['a:E-Mail-Domain'])
|
||||
assert.deepEqual(hit({ email: 'jemand@gmail.com' }), [])
|
||||
assert.deepEqual(hit({ phone: '06301/9999' }), ['c:Telefon'])
|
||||
assert.deepEqual(hit({ name: 'Autohaus Schmidt' }), ['c:Name']) // Teilname ab 2 Wörtern und 8 Zeichen
|
||||
assert.deepEqual(hit({ name: 'Schmidt' }), [])
|
||||
assert.deepEqual(hit({ name: 'Bäckerei Müller', phone: '0049 6301 9999' }), ['c:Telefon', 'a:Name'])
|
||||
assert.deepEqual(hit({ name: 'bäckerei müller' }, { excludeId: 'a' }), [])
|
||||
assert.deepEqual(hit({}), [])
|
||||
assert.deepEqual(findDuplicates(undefined, { name: 'x' }), [])
|
||||
})
|
||||
|
||||
test('Absage-Vermerk: Zeile, Notizen, letzter Vermerk', () => {
|
||||
const d = new Date(2026, 9, 6, 12)
|
||||
assert.equal(rejectionLine({ date: d, by: 'Kenso Grimm', reason: ' kein\n Bedarf ' }), 'Abgesagt am 06.10.2026 (Kenso Grimm): kein Bedarf')
|
||||
assert.equal(rejectionLine({ date: d }), 'Abgesagt am 06.10.2026')
|
||||
assert.equal(notesWithRejection('', 'X'), 'X')
|
||||
assert.equal(notesWithRejection(' alt \n', 'X'), 'X\nalt')
|
||||
assert.equal(notesWithRejection('a'.repeat(5000), 'X').length, 5000)
|
||||
assert.equal(lastRejection(LIST[2].notes), 'Abgesagt am 01.10.2026 (Kenso): kein Bedarf')
|
||||
assert.equal(lastRejection('nur Notiz'), '')
|
||||
assert.equal(lastRejection(undefined), '')
|
||||
})
|
||||
Reference in new issue
Block a user