fdd8247527
fake()->unique() builds a fresh UniqueGenerator per definition() call, so uniqueness is not guaranteed within a batch — names collided on the (tenant_id, name) UNIQUE under pest --parallel. Append Str::random(8) (62^8 ≈ 2e14 space) to eliminate the collision. Verified: ProjectBulkActions 15/15 ×2 parallel runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
87 lines
2.8 KiB
PHP
87 lines
2.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Database\Factories;
|
|
|
|
use App\Models\Project;
|
|
use App\Models\Tenant;
|
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
|
use Illuminate\Support\Str;
|
|
|
|
/**
|
|
* @extends Factory<Project>
|
|
*/
|
|
class ProjectFactory extends Factory
|
|
{
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function definition(): array
|
|
{
|
|
return [
|
|
'tenant_id' => Tenant::factory(),
|
|
// Квирк #77: fake()->unique() создаёт новый UniqueGenerator на каждый
|
|
// definition()-call → history между вызовами не сохраняется, uniqueness
|
|
// внутри batch не гарантирована (коллизия (tenant_id, name) UNIQUE в
|
|
// pest --parallel). Str::random(8) суффикс (62^8 ≈ 2e14) гасит коллизию.
|
|
'name' => fake()->words(3, true).' '.Str::random(8),
|
|
'type' => 'webhook',
|
|
'is_active' => true,
|
|
'daily_limit_target' => 10,
|
|
'region_mask' => 255,
|
|
'region_mode' => 'include',
|
|
'delivery_days_mask' => 127,
|
|
'assignment_strategy' => 'manual',
|
|
'ttfr_target_minutes' => 15,
|
|
// Supplier integration (Plan 1/5 Task 1+10) — по умолчанию пусто,
|
|
// CHECK constraint'ы projects не нарушаются (signal_type IS NULL → все остальные опциональны).
|
|
'signal_type' => null,
|
|
'signal_identifier' => null,
|
|
'sms_senders' => null,
|
|
'sms_keyword' => null,
|
|
'delivered_in_month' => 0,
|
|
'supplier_b1_project_id' => null,
|
|
'supplier_b2_project_id' => null,
|
|
'supplier_b3_project_id' => null,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Сайт-сигнал с указанным доменом.
|
|
*/
|
|
public function asSiteSignal(string $domain): self
|
|
{
|
|
return $this->state([
|
|
'signal_type' => 'site',
|
|
'signal_identifier' => $domain,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Звонок-сигнал с указанным телефоном.
|
|
*/
|
|
public function asCallSignal(string $phone): self
|
|
{
|
|
return $this->state([
|
|
'signal_type' => 'call',
|
|
'signal_identifier' => $phone,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* SMS-сигнал. Sender-имена в массиве (1+); keyword optional (пусто = только B3).
|
|
*
|
|
* @param array<int, string> $senders
|
|
*/
|
|
public function asSmsSignal(array $senders, ?string $keyword = null): self
|
|
{
|
|
return $this->state([
|
|
'signal_type' => 'sms',
|
|
'signal_identifier' => null,
|
|
'sms_senders' => $senders,
|
|
'sms_keyword' => $keyword,
|
|
]);
|
|
}
|
|
}
|