'U '.uniqid(), 'email' => 'in'.uniqid().'@s.local', 'password' => bcrypt('secret'), 'role' => $role, 'is_active' => $active, ]); } function int_head(): SalesUser { return SalesUser::where('role', 'head')->first() ?? int_user('head'); } beforeEach(function () { config(['sales.integration_token' => 'TESTTOKEN']); }); // ── сервис-токен ─────────────────────────────────────────────────────────────── test('без токена сервис-канал → 401', function () { $this->getJson('/api/sales/integration/managers')->assertUnauthorized(); }); test('неверный токен → 401', function () { $this->getJson('/api/sales/integration/managers', ['X-Sales-Token' => 'WRONG']) ->assertUnauthorized(); }); test('пустой конфиг-токен → 401 даже при совпадении пустого', function () { config(['sales.integration_token' => null]); $this->getJson('/api/sales/integration/managers', ['X-Sales-Token' => '']) ->assertUnauthorized(); }); // ── managers ──────────────────────────────────────────────────────────────────── test('managers отдаёт активных менеджеров (вкл. начальника), неактивных нет', function () { $head = int_head(); $m = int_user('manager', true); $off = int_user('manager', false); $res = $this->getJson('/api/sales/integration/managers', ['X-Sales-Token' => 'TESTTOKEN']); $res->assertOk(); $ids = collect($res->json('data'))->pluck('id'); expect($ids)->toContain($head->id)->toContain($m->id)->not->toContain($off->id); }); // ── ingest ──────────────────────────────────────────────────────────────────── test('ingest создаёт карточку stage=new с payload; повтор по inn → skipped, payload обновлён', function () { $head = int_head(); $mgr = int_user('manager'); $body = ['sales_user_id' => $mgr->id, 'firms' => [[ 'firm_name' => 'ООО Ромашка', 'city' => 'Ростов', 'phone' => '+7863', 'site' => 'romashka.ru', 'inn' => '6161234567', 'rating_label' => 'горячая', 'payload' => ['director' => 'Иванов'], ]]]; $r1 = $this->postJson('/api/sales/integration/prospects', $body, ['X-Sales-Token' => 'TESTTOKEN']); $r1->assertOk(); expect($r1->json('created'))->toBe(1); $p = SalesProspect::where('sales_user_id', $mgr->id)->where('inn', '6161234567')->first(); expect($p->stage)->toBe('new'); expect($p->payload['director'])->toBe('Иванов'); // Повтор той же фирмы → не плодит, payload обновляется. $body['firms'][0]['payload'] = ['director' => 'Петров']; $r2 = $this->postJson('/api/sales/integration/prospects', $body, ['X-Sales-Token' => 'TESTTOKEN']); $r2->assertOk(); expect($r2->json('skipped'))->toBe(1); expect(SalesProspect::where('sales_user_id', $mgr->id)->where('inn', '6161234567')->count())->toBe(1); expect(SalesProspect::where('sales_user_id', $mgr->id)->where('inn', '6161234567')->first()->payload['director']) ->toBe('Петров'); }); test('ingest дедуп по phone, когда inn пуст', function () { $mgr = int_user('manager'); int_head(); $body = ['sales_user_id' => $mgr->id, 'firms' => [[ 'firm_name' => 'Без ИНН', 'city' => 'Ростов', 'phone' => '+79990001122', 'site' => null, 'inn' => null, 'rating_label' => null, 'payload' => [], ]]]; $this->postJson('/api/sales/integration/prospects', $body, ['X-Sales-Token' => 'TESTTOKEN'])->assertOk(); $r2 = $this->postJson('/api/sales/integration/prospects', $body, ['X-Sales-Token' => 'TESTTOKEN']); expect($r2->json('skipped'))->toBe(1); expect(SalesProspect::where('sales_user_id', $mgr->id)->where('phone', '+79990001122')->count())->toBe(1); }); test('ingest: невалидный sales_user_id → 422', function () { int_head(); $this->postJson('/api/sales/integration/prospects', [ 'sales_user_id' => 999999, 'firms' => [['firm_name' => 'X']], ], ['X-Sales-Token' => 'TESTTOKEN'])->assertStatus(422); }); test('ingest проставляет assigned_by = начальник', function () { $head = int_head(); $mgr = int_user('manager'); $this->postJson('/api/sales/integration/prospects', [ 'sales_user_id' => $mgr->id, 'firms' => [['firm_name' => 'A', 'inn' => '7700000001', 'payload' => []]], ], ['X-Sales-Token' => 'TESTTOKEN'])->assertOk(); $p = SalesProspect::where('sales_user_id', $mgr->id)->where('inn', '7700000001')->first(); expect($p->assigned_by)->toBe($head->id); });