Files
portal/app/tests/Frontend/SalesProspectCreateDialog.spec.ts
T
Дмитрий f0a7de7d5d fix(портал продаж): ИНН при заведении кандидата больше не обязателен
Обязательно только название фирмы — вывеска. ИНН по желанию: заполнили
проверяем контрольную сумму и ловим дубль, оставили пустым — карточка
заводится без ИНН. Пустая строка приравнена к «не знаю» и уходит как null.

Проверка на дубль по ИНН теперь срабатывает только когда ИНН указан:
раньше вторая карточка без ИНН у того же менеджера ложно ловилась бы как
«эта фирма уже есть в вашей воронке». Схему БД менять не потребовалось —
колонка inn была nullable, уникальный индекс частичный.

Тесты: сервер 58/58, экран 9/9. Спека §16 обновлена.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 09:51:52 +03:00

157 lines
5.4 KiB
TypeScript

import { mount, flushPromises } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import { describe, expect, it, vi, beforeEach } from 'vitest';
vi.mock('../../resources/js/api/sales', () => ({
lookupProspectInn: vi.fn(),
}));
import SalesProspectCreateDialog from '../../resources/js/components/sales/SalesProspectCreateDialog.vue';
import { lookupProspectInn } from '../../resources/js/api/sales';
const vuetify = createVuetify();
interface Contact {
name: string;
position: string;
phones: string[];
}
interface CreateVm {
firmName: string;
legalName: string;
city: string;
site: string;
inn: string;
notes: string;
contacts: Contact[];
error: string;
lookingUp: boolean;
lookup: () => Promise<void>;
submit: () => void;
}
function factory() {
const w = mount(SalesProspectCreateDialog, {
props: { modelValue: true },
global: { plugins: [vuetify] },
});
return { w, vm: w.vm as unknown as CreateVm };
}
describe('SalesProspectCreateDialog', () => {
beforeEach(() => vi.clearAllMocks());
it('без названия не эмитит create', () => {
const { w, vm } = factory();
vm.inn = '5501234560';
vm.submit();
expect(w.emitted('create')).toBeFalsy();
});
it('без ИНН карточка всё равно заводится (обязательно только название)', () => {
const { w, vm } = factory();
vm.firmName = 'Без ИНН';
vm.submit();
expect(w.emitted('create')?.[0]?.[0]).toMatchObject({ firm_name: 'Без ИНН', inn: null });
expect(vm.error).toBe('');
});
it('кривой ИНН не пускаем дальше формы', () => {
const { w, vm } = factory();
vm.firmName = 'Опечатка';
vm.inn = '5501234567';
vm.submit();
expect(w.emitted('create')).toBeFalsy();
});
it('эмитит create с брендом, юрлицом и контактными лицами', () => {
const { w, vm } = factory();
vm.firmName = 'Стоматология «Улыбка»';
vm.legalName = 'ООО «Улыбка Плюс»';
vm.inn = '5501234560';
vm.city = 'Омск';
vm.contacts = [
{ name: 'Иванов Иван', position: 'директор', phones: ['+79130000001', '+73812000002'] },
];
vm.submit();
expect(w.emitted('create')?.[0]?.[0]).toMatchObject({
firm_name: 'Стоматология «Улыбка»',
legal_name: 'ООО «Улыбка Плюс»',
inn: '5501234560',
city: 'Омск',
contacts: [
{ name: 'Иванов Иван', position: 'директор', phones: ['+79130000001', '+73812000002'] },
],
});
});
it('пустые контакты и телефоны не уходят на сервер', () => {
const { w, vm } = factory();
vm.firmName = 'Полупустые';
vm.inn = '5501234560';
vm.contacts = [
{ name: ' ', position: '', phones: ['', ' '] },
{ name: 'Сидоров', position: '', phones: ['+79130000009', ''] },
];
vm.submit();
const payload = w.emitted('create')?.[0]?.[0] as Record<string, unknown>;
expect(payload.contacts).toEqual([{ name: 'Сидоров', position: null, phones: ['+79130000009'] }]);
});
it('поиск по ИНН подставляет юрлицо и город', async () => {
(lookupProspectInn as ReturnType<typeof vi.fn>).mockResolvedValue({
found: true,
legal_name: 'ООО «Улыбка Плюс»',
city: 'Омск',
});
const { vm } = factory();
vm.inn = '5501234560';
await vm.lookup();
await flushPromises();
expect(lookupProspectInn).toHaveBeenCalledWith('5501234560');
expect(vm.legalName).toBe('ООО «Улыбка Плюс»');
expect(vm.city).toBe('Омск');
});
it('поиск не затирает город, если менеджер уже вписал свой', async () => {
(lookupProspectInn as ReturnType<typeof vi.fn>).mockResolvedValue({
found: true,
legal_name: 'ООО «Улыбка Плюс»',
city: 'Омск',
});
const { vm } = factory();
vm.inn = '5501234560';
vm.city = 'Красноярск';
await vm.lookup();
await flushPromises();
expect(vm.city).toBe('Красноярск');
});
it('ДаData не нашла → говорим об этом и не мешаем заполнить руками', async () => {
(lookupProspectInn as ReturnType<typeof vi.fn>).mockResolvedValue({ found: false });
const { vm } = factory();
vm.inn = '5501234560';
await vm.lookup();
await flushPromises();
expect(vm.error).not.toBe('');
expect(vm.legalName).toBe('');
expect(vm.lookingUp).toBe(false);
});
it('ДаData упала → форма остаётся рабочей', async () => {
(lookupProspectInn as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('network'));
const { vm } = factory();
vm.inn = '5501234560';
await vm.lookup();
await flushPromises();
expect(vm.lookingUp).toBe(false);
});
});