19f319cd5d
- ReportJob Eloquent (schema §13.5 report_jobs): status pending/processing/done/failed,
parameters JSONB (format/date_from/date_to/project_id?/manager_id?), constants
TYPES + FORMATS, helpers isActive/isDone/isFailed.
- ReportJobFactory + states processing/done/failed.
- App\Services\Reports\* пакет: ReportGenerator interface, GenerationResult DTO,
ReportGeneratorRegistry с резолвом по (type,format), DealsExportCsvGenerator
(Excel-friendly CSV: BOM, ; separator, \r\n, escape; deals JOIN projects/users/
supplier_lead_costs за date_from..date_to, soft-deleted скрыты).
- App\Jobs\GenerateReportJob: tries=1 (auto-retry отключён, retry через UI кнопку
CTO-6); меняет status pending → processing → done|failed, заполняет file_path/
file_size/generation_seconds/finished_at/expires_at (=NOW+30д).
- App\Http\Controllers\Api\ReportJobController под auth:sanctum:
- GET /api/reports/jobs?status=&limit=&offset= → jobs+total+counts+quota
- GET /api/reports/jobs/{id}
- POST /api/reports/jobs (квота CTO-7: max 3 active per tenant → 422)
- dispatch GenerateReportJob (sync на dev → файл готов сразу).
- Storage local-disk на dev (storage/app/reports/{tenant_id}/{job_id}.csv);
на prod заменим на s3 (Yandex Object Storage) отдельным коммитом.
- Pest +20 в tests/Feature/Reports/ReportJobControllerTest.php (всего 379/379,
1280 assertions): 401 без auth / GET пустой+only-own+ORDER+filter+counts+limit/
show success+404 own/foreign / store 422 (без полей/неизвестный type/date_to<from)/
dispatch / sync queue → done с file (BOM проверен) / unsupported format → failed/
квота 3 → 422 на 4-м / квота не считает done+failed / квота per-tenant.
- phpstan-baseline регенерирован (+1 ignored для Factory typing).
Этап 1/4 эпика Reports backend (закрыт). Этап 2: 4 типа × 4 формата.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
59 lines
1.5 KiB
PHP
59 lines
1.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Database\Factories;
|
|
|
|
use App\Models\ReportJob;
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
/**
|
|
* @extends Factory<ReportJob>
|
|
*/
|
|
class ReportJobFactory extends Factory
|
|
{
|
|
public function definition(): array
|
|
{
|
|
return [
|
|
'tenant_id' => Tenant::factory(),
|
|
'user_id' => User::factory()->state(fn (array $attrs, ReportJob $r) => ['tenant_id' => $r->tenant_id]),
|
|
'type' => 'deals_export',
|
|
'parameters' => [
|
|
'format' => 'csv',
|
|
'date_from' => Carbon::now()->subMonth()->toDateString(),
|
|
'date_to' => Carbon::now()->toDateString(),
|
|
],
|
|
'status' => ReportJob::STATUS_PENDING,
|
|
];
|
|
}
|
|
|
|
public function processing(): static
|
|
{
|
|
return $this->state(['status' => ReportJob::STATUS_PROCESSING]);
|
|
}
|
|
|
|
public function done(): static
|
|
{
|
|
return $this->state([
|
|
'status' => ReportJob::STATUS_DONE,
|
|
'file_path' => 'reports/1/123.csv',
|
|
'file_size' => 1024,
|
|
'generation_seconds' => 2,
|
|
'finished_at' => Carbon::now(),
|
|
'expires_at' => Carbon::now()->addDays(30),
|
|
]);
|
|
}
|
|
|
|
public function failed(): static
|
|
{
|
|
return $this->state([
|
|
'status' => ReportJob::STATUS_FAILED,
|
|
'error_message' => 'S3 timeout',
|
|
'finished_at' => Carbon::now(),
|
|
]);
|
|
}
|
|
}
|