feat(pd): pd_processing_log 'created' on historical import (152-ФЗ)
This commit is contained in:
@@ -10,6 +10,7 @@ use App\Models\ImportUnknownStatus;
|
||||
use App\Models\Project;
|
||||
use App\Models\Reminder;
|
||||
use App\Services\MonthlyPartitionManager;
|
||||
use App\Services\Pd\PdAuditLogger;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Throwable;
|
||||
@@ -26,6 +27,7 @@ final class HistoricalImportService
|
||||
public function __construct(
|
||||
private readonly MonthlyPartitionManager $partitions,
|
||||
private readonly StatusRuToSlugMapper $statusMapper,
|
||||
private readonly PdAuditLogger $pdLog,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -68,7 +70,7 @@ final class HistoricalImportService
|
||||
}
|
||||
|
||||
try {
|
||||
$wasCreated = $this->upsertRow($tenantId, $userId, $row, $slug);
|
||||
$wasCreated = $this->upsertRow($tenantId, $userId, $row, $slug, $log->id);
|
||||
$wasCreated ? $added++ : $updated++;
|
||||
} catch (Throwable $e) {
|
||||
$skipped++;
|
||||
@@ -132,9 +134,9 @@ final class HistoricalImportService
|
||||
* Идемпотентный upsert одной строки в собственной транзакции.
|
||||
* Возвращает true — создана новая сделка, false — обновлена существующая.
|
||||
*/
|
||||
private function upsertRow(int $tenantId, int $userId, ParsedLeadRow $row, string $slug): bool
|
||||
private function upsertRow(int $tenantId, int $userId, ParsedLeadRow $row, string $slug, int $importLogId): bool
|
||||
{
|
||||
return DB::transaction(function () use ($tenantId, $userId, $row, $slug): bool {
|
||||
return DB::transaction(function () use ($tenantId, $userId, $row, $slug, $importLogId): bool {
|
||||
DB::statement('SET LOCAL app.current_tenant_id = '.$tenantId);
|
||||
|
||||
$project = Project::firstOrCreate(
|
||||
@@ -188,6 +190,17 @@ final class HistoricalImportService
|
||||
|
||||
$this->syncReminder($tenantId, $userId, $deal, $row);
|
||||
|
||||
$this->pdLog->record(
|
||||
action: 'created',
|
||||
subjectType: 'lead',
|
||||
subjectId: $deal->id,
|
||||
purpose: 'lead_create_import_'.$importLogId,
|
||||
tenantId: $tenantId,
|
||||
actorTenantUserId: $userId,
|
||||
actorAdminUserId: null,
|
||||
ip: null,
|
||||
);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 152-ФЗ: pd_processing_log 'created' записывается при создании сделки
|
||||
* через исторический импорт (HistoricalImportService).
|
||||
*/
|
||||
|
||||
use App\Models\ImportLog;
|
||||
use App\Models\Tenant;
|
||||
use App\Models\User;
|
||||
use App\Services\Import\CsvLeadsParser;
|
||||
use App\Services\Import\HistoricalImportService;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
uses(DatabaseTransactions::class);
|
||||
|
||||
it('writes pd_processing_log created on historical import for each new deal', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$user = User::factory()->for($tenant)->create();
|
||||
DB::statement('SET app.current_tenant_id = '.$tenant->id);
|
||||
|
||||
$log = ImportLog::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'user_id' => $user->id,
|
||||
'filename' => 'leads.csv',
|
||||
'file_path' => 'imports/x.csv',
|
||||
'dry_run' => false,
|
||||
]);
|
||||
|
||||
$header = "\xEF\xBB\xBF".'id,Проект,Тег проекта,Телефон,Создано,Напоминание,Комментарий,Состояние,Имя';
|
||||
$rows = array_merge(
|
||||
(new CsvLeadsParser)->parse($header."\n".'9901,Окна,окна,79161000001,2023/07/10 10:00:00,,,Новые,')->rows,
|
||||
(new CsvLeadsParser)->parse($header."\n".'9902,Окна,окна,79161000002,2023/07/10 10:00:00,,,Новые,')->rows,
|
||||
);
|
||||
|
||||
app(HistoricalImportService::class)->import($tenant->id, $user->id, $log, $rows);
|
||||
|
||||
$pd = DB::table('pd_processing_log')
|
||||
->where('action', 'created')
|
||||
->where('purpose', 'lead_create_import_'.$log->id)
|
||||
->get();
|
||||
|
||||
expect($pd)->toHaveCount(2);
|
||||
|
||||
foreach ($pd as $r) {
|
||||
expect($r->subject_type)->toBe('lead')
|
||||
->and((int) $r->actor_tenant_user_id)->toBe($user->id)
|
||||
->and($r->actor_admin_user_id)->toBeNull()
|
||||
->and($r->subject_id)->not->toBeNull()
|
||||
->and((int) $r->tenant_id)->toBe($tenant->id);
|
||||
}
|
||||
});
|
||||
|
||||
it('does NOT write pd_processing_log on dry_run import', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$user = User::factory()->for($tenant)->create();
|
||||
DB::statement('SET app.current_tenant_id = '.$tenant->id);
|
||||
|
||||
$log = ImportLog::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'user_id' => $user->id,
|
||||
'filename' => 'leads.csv',
|
||||
'file_path' => 'imports/x.csv',
|
||||
'dry_run' => true,
|
||||
]);
|
||||
|
||||
$header = "\xEF\xBB\xBF".'id,Проект,Тег проекта,Телефон,Создано,Напоминание,Комментарий,Состояние,Имя';
|
||||
$rows = (new CsvLeadsParser)->parse($header."\n".'9903,Окна,окна,79161000003,2023/07/10 10:00:00,,,Новые,')->rows;
|
||||
|
||||
app(HistoricalImportService::class)->import($tenant->id, $user->id, $log, $rows);
|
||||
|
||||
$count = DB::table('pd_processing_log')
|
||||
->where('purpose', 'lead_create_import_'.$log->id)
|
||||
->count();
|
||||
|
||||
expect($count)->toBe(0);
|
||||
});
|
||||
|
||||
it('does NOT write pd_processing_log on import UPDATE (idempotent re-import)', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$user = User::factory()->for($tenant)->create();
|
||||
DB::statement('SET app.current_tenant_id = '.$tenant->id);
|
||||
|
||||
$header = "\xEF\xBB\xBF".'id,Проект,Тег проекта,Телефон,Создано,Напоминание,Комментарий,Состояние,Имя';
|
||||
|
||||
// First import — creates the deal
|
||||
$log1 = ImportLog::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'user_id' => $user->id,
|
||||
'filename' => 'leads.csv',
|
||||
'file_path' => 'imports/x.csv',
|
||||
'dry_run' => false,
|
||||
]);
|
||||
$rows1 = (new CsvLeadsParser)->parse($header."\n".'9904,Окна,окна,79161000004,2023/07/10 10:00:00,,,Новые,')->rows;
|
||||
app(HistoricalImportService::class)->import($tenant->id, $user->id, $log1, $rows1);
|
||||
|
||||
// Second import — updates the same deal
|
||||
$log2 = ImportLog::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'user_id' => $user->id,
|
||||
'filename' => 'leads2.csv',
|
||||
'file_path' => 'imports/x2.csv',
|
||||
'dry_run' => false,
|
||||
]);
|
||||
$rows2 = (new CsvLeadsParser)->parse($header."\n".'9904,Окна,окна,79161000004,2023/07/10 10:00:00,,,Оплачено,')->rows;
|
||||
app(HistoricalImportService::class)->import($tenant->id, $user->id, $log2, $rows2);
|
||||
|
||||
// Only the first import wrote a pd log entry
|
||||
$countLog1 = DB::table('pd_processing_log')
|
||||
->where('action', 'created')
|
||||
->where('purpose', 'lead_create_import_'.$log1->id)
|
||||
->count();
|
||||
$countLog2 = DB::table('pd_processing_log')
|
||||
->where('action', 'created')
|
||||
->where('purpose', 'lead_create_import_'.$log2->id)
|
||||
->count();
|
||||
|
||||
expect($countLog1)->toBe(1)
|
||||
->and($countLog2)->toBe(0);
|
||||
});
|
||||
Reference in New Issue
Block a user