From aa37f4cbedd6fb6c3be8d58cb270bb4bb781041e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Sun, 10 May 2026 18:24:45 +0300 Subject: [PATCH] feat(models): SupplierLead model + factory (raw-payload incoming webhooks) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SaaS-level модель для supplier_leads (Plan 2/5 Task 2). belongsTo(SupplierProject) + array cast на raw_payload + datetime *_at. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/app/Models/SupplierLead.php | 66 +++++++++++++++++++ .../factories/SupplierLeadFactory.php | 44 +++++++++++++ app/tests/Feature/Models/SupplierLeadTest.php | 53 +++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 app/app/Models/SupplierLead.php create mode 100644 app/database/factories/SupplierLeadFactory.php create mode 100644 app/tests/Feature/Models/SupplierLeadTest.php diff --git a/app/app/Models/SupplierLead.php b/app/app/Models/SupplierLead.php new file mode 100644 index 00000000..3f402e58 --- /dev/null +++ b/app/app/Models/SupplierLead.php @@ -0,0 +1,66 @@ + */ + use HasFactory; + + protected $table = 'supplier_leads'; + + public $timestamps = false; + + protected $fillable = [ + 'supplier_project_id', + 'platform', + 'raw_payload', + 'vid', + 'phone', + 'received_at', + 'source', + 'processed_at', + 'deals_created_count', + 'error', + ]; + + protected function casts(): array + { + return [ + 'raw_payload' => 'array', + 'received_at' => 'datetime', + 'processed_at' => 'datetime', + 'vid' => 'integer', + 'deals_created_count' => 'integer', + ]; + } + + /** @return BelongsTo */ + public function supplierProject(): BelongsTo + { + return $this->belongsTo(SupplierProject::class); + } + + protected static function newFactory(): SupplierLeadFactory + { + return SupplierLeadFactory::new(); + } +} diff --git a/app/database/factories/SupplierLeadFactory.php b/app/database/factories/SupplierLeadFactory.php new file mode 100644 index 00000000..717cab07 --- /dev/null +++ b/app/database/factories/SupplierLeadFactory.php @@ -0,0 +1,44 @@ + + */ +class SupplierLeadFactory extends Factory +{ + protected $model = SupplierLead::class; + + public function definition(): array + { + $platform = $this->faker->randomElement(['B1', 'B2', 'B3']); + $vid = $this->faker->unique()->numberBetween(100_000_000, 999_999_999); + $phone = '7'.$this->faker->numerify('##########'); + + return [ + 'supplier_project_id' => SupplierProject::factory(), + 'platform' => $platform, + 'raw_payload' => [ + 'vid' => $vid, + 'project' => $platform.'_test.example.com', + 'tag' => 'test-tag', + 'phone' => $phone, + 'phones' => [$phone], + 'time' => now()->getTimestamp(), + ], + 'vid' => $vid, + 'phone' => $phone, + 'received_at' => now(), + 'source' => 'webhook', + 'processed_at' => null, + 'deals_created_count' => null, + 'error' => null, + ]; + } +} diff --git a/app/tests/Feature/Models/SupplierLeadTest.php b/app/tests/Feature/Models/SupplierLeadTest.php new file mode 100644 index 00000000..e310afb1 --- /dev/null +++ b/app/tests/Feature/Models/SupplierLeadTest.php @@ -0,0 +1,53 @@ +create([ + 'platform' => 'B1', + 'signal_type' => 'site', + 'unique_key' => 'vashinvestor.ru', + ]); + + $lead = SupplierLead::factory()->create([ + 'supplier_project_id' => $supplierProject->id, + 'platform' => 'B1', + ]); + + expect($lead->id)->toBeInt()->toBeGreaterThan(0); + expect($lead->platform)->toBe('B1'); + expect($lead->raw_payload)->toBeArray(); + expect($lead->received_at)->toBeInstanceOf(Carbon::class); +}); + +it('belongsTo supplier_project', function (): void { + $supplierProject = SupplierProject::factory()->create(); + $lead = SupplierLead::factory()->create([ + 'supplier_project_id' => $supplierProject->id, + ]); + + expect($lead->supplierProject->id)->toBe($supplierProject->id); +}); + +it('casts raw_payload to array and *_at to Carbon', function (): void { + $lead = SupplierLead::factory()->create([ + 'raw_payload' => ['vid' => 12345, 'project' => 'B1_test.ru'], + 'processed_at' => now(), + ]); + + expect($lead->raw_payload)->toBe(['vid' => 12345, 'project' => 'B1_test.ru']); + expect($lead->processed_at)->toBeInstanceOf(Carbon::class); +}); + +it('factory respects deals_created_count nullable default', function (): void { + $lead = SupplierLead::factory()->create(); + expect($lead->deals_created_count)->toBeNull(); + expect($lead->processed_at)->toBeNull(); +});