8f2b82405a
Парсер CSV-выгрузки лидов crm.bp-gr.ru (ТЗ §6.2/§6.3): срезает UTF-8 BOM, разбирает строки через str_getcsv, валидирует телефон (7XXXXXXXXXX) и даты (Y/m/d H:i:s), срезает префикс B[123]_ из названия проекта. Невалидные строки не роняют парсинг — собираются в errors[] с абсолютным номером строки. Тесты: 5/5 (unit, без DB). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
2.9 KiB
PHP
71 lines
2.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Services\Import\CsvLeadsParser;
|
|
|
|
function csvLeads(string $body, bool $withBom = true): string
|
|
{
|
|
$header = 'id,Проект,Тег проекта,Телефон,Создано,Напоминание,Комментарий,Состояние,Имя';
|
|
|
|
return ($withBom ? "\xEF\xBB\xBF" : '').$header."\n".$body;
|
|
}
|
|
|
|
test('парсит валидную строку в ParsedLeadRow', function (): void {
|
|
$result = (new CsvLeadsParser)->parse(csvLeads(
|
|
'1001,B1_Окна,окна,79161234567,2024/03/15 10:30:00,,Тёплый клиент,Переговоры,Иван'
|
|
));
|
|
|
|
expect($result->rows)->toHaveCount(1)
|
|
->and($result->errors)->toBeEmpty();
|
|
|
|
$row = $result->rows[0];
|
|
expect($row->sourceCrmId)->toBe(1001)
|
|
->and($row->projectName)->toBe('Окна') // префикс B1_ срезан
|
|
->and($row->projectTag)->toBe('окна')
|
|
->and($row->phone)->toBe('79161234567')
|
|
->and($row->statusRu)->toBe('Переговоры')
|
|
->and($row->contactName)->toBe('Иван')
|
|
->and($row->reminderAt)->toBeNull()
|
|
->and($row->receivedAt->format('Y-m-d H:i:s'))->toBe('2024-03-15 10:30:00');
|
|
});
|
|
|
|
test('срезает BOM и не считает заголовок строкой данных', function (): void {
|
|
$result = (new CsvLeadsParser)->parse(csvLeads(
|
|
'1,Проект,тег,79990001122,2024/01/01 00:00:00,,,Новые,'
|
|
));
|
|
|
|
expect($result->rows)->toHaveCount(1)
|
|
->and($result->rows[0]->sourceCrmId)->toBe(1);
|
|
});
|
|
|
|
test('парсит напоминание когда оно непустое', function (): void {
|
|
$result = (new CsvLeadsParser)->parse(csvLeads(
|
|
'2,П,т,79990001122,2024/01/01 09:00:00,2024/01/05 12:00:00,,Новые,'
|
|
));
|
|
|
|
expect($result->rows[0]->reminderAt?->format('Y-m-d H:i:s'))->toBe('2024-01-05 12:00:00');
|
|
});
|
|
|
|
test('собирает ошибки невалидного телефона и даты, не роняя парсинг', function (): void {
|
|
$result = (new CsvLeadsParser)->parse(csvLeads(
|
|
"3,П,т,8916123,2024/01/01 00:00:00,,,Новые,\n".
|
|
"4,П,т,79990001122,НЕ-ДАТА,,,Новые,\n".
|
|
'5,П,т,79990001133,2024/01/02 00:00:00,,,Новые,'
|
|
));
|
|
|
|
expect($result->rows)->toHaveCount(1) // только строка 5 валидна
|
|
->and($result->rows[0]->sourceCrmId)->toBe(5)
|
|
->and($result->errors)->toHaveCount(2);
|
|
|
|
expect($result->errors[0]['line'])->toBe(2); // 1-я data-строка (после header)
|
|
});
|
|
|
|
test('обрабатывает кавычки и запятые внутри поля', function (): void {
|
|
$result = (new CsvLeadsParser)->parse(csvLeads(
|
|
'6,П,т,79990001122,2024/01/01 00:00:00,,"Комментарий, с запятой",Новые,Пётр'
|
|
));
|
|
|
|
expect($result->rows[0]->comment)->toBe('Комментарий, с запятой');
|
|
});
|