ac186593f2
Закрывает gap «UI меняет статус, но изменения не сохраняются на backend»
из v1.51. Reload-btn заменяет polling/SSE до прихода long-poll'а на prod.
Backend (DealController::transition):
- POST /api/deals/transition {tenant_id, ids: [1..1000 ints], status}.
- Валидация status — exists в lead_statuses (глобальная таблица).
- RLS-обёртка SET LOCAL + defense-in-depth where(tenant_id) для
partial-update: чужие id остаются в исходном статусе.
- ActivityLog event=deal.status_changed с context={from, to, source: 'bulk'}
для каждой ИЗМЕНЁННОЙ сделки. NO-OP (старый==новый) не пишется в audit.
- Ответ: {updated, requested, status}.
Pest +7 (DealTransitionTest):
- 422 missing fields / 404 unknown tenant / 422 неизвестный slug + не апдейт /
batch update 3 сделок + 3 ActivityLog с правильным context /
NO-OP не пишет ActivityLog / defense-in-depth (2 tenant'а — обновляется
только свой) / 422 пустой массив ids.
Frontend:
- dealsApi.transitionDeals — типизированный helper с ensureCsrfCookie.
- applyBulkStatus в DealsView переписан async: optimistic local-update +
backend-вызов если auth.user.tenant_id. На success — toast «Обновлено
N из M.», на fail — warning toast + локальный update НЕ откатывается.
Без auth.user — только optimistic (legacy local-mode сохранён).
- reload-btn в DealsView и KanbanView — outlined «Обновить» mdi-refresh,
привязан к loadDeals. В DealsView :loading="loading" во время fetch'а.
Vitest +5:
- reload-btn (Deals + Kanban) — listDeals вызывается дважды.
- applyBulkStatus с tenant_id — transitionDeals + optimistic + toast.
- applyBulkStatus без tenant_id — НЕ вызывается transitionDeals.
- applyBulkStatus reject — toast warning + локальный update остаётся.
PHPStan baseline регенерирован. cspell-glossary +апдейт*.
Регресс:
- Lint+type-check+format passed.
- Vitest 266/266 за 18.16 сек (+5 от 261).
- Vite build 1.06 сек.
- Pint + PHPStan passed.
- Pest 193/193 за 23.27 сек (+7 от 186, 767 assertions).
Реестр v1.60→v1.61 / CLAUDE.md v1.51→v1.52.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
440 lines
14 KiB
Plaintext
440 lines
14 KiB
Plaintext
parameters:
|
|
ignoreErrors:
|
|
-
|
|
message: '#^Using nullsafe method call on non\-nullable type Illuminate\\Support\\Carbon\. Use \-\> instead\.$#'
|
|
identifier: nullsafe.neverNull
|
|
count: 1
|
|
path: app/Http/Controllers/Api/DealController.php
|
|
|
|
-
|
|
message: '#^Cannot call method toIso8601String\(\) on null\.$#'
|
|
identifier: method.nonObject
|
|
count: 1
|
|
path: app/Http/Controllers/Api/ImpersonationController.php
|
|
|
|
-
|
|
message: '#^Strict comparison using \!\=\= between int and null will always evaluate to true\.$#'
|
|
identifier: notIdentical.alwaysTrue
|
|
count: 1
|
|
path: app/Http/Middleware/SetTenantContext.php
|
|
|
|
-
|
|
message: '#^Return type \(array\<string, mixed\>\) of method Database\\Factories\\ProjectFactory\:\:definition\(\) should be compatible with return type \(array\<model property of App\\Models\\Project, mixed\>\) of method Illuminate\\Database\\Eloquent\\Factories\\Factory\<App\\Models\\Project\>\:\:definition\(\)$#'
|
|
identifier: method.childReturnType
|
|
count: 1
|
|
path: database/factories/ProjectFactory.php
|
|
|
|
-
|
|
message: '#^Return type \(array\<string, mixed\>\) of method Database\\Factories\\TenantFactory\:\:definition\(\) should be compatible with return type \(array\<model property of App\\Models\\Tenant, mixed\>\) of method Illuminate\\Database\\Eloquent\\Factories\\Factory\<App\\Models\\Tenant\>\:\:definition\(\)$#'
|
|
identifier: method.childReturnType
|
|
count: 1
|
|
path: database/factories/TenantFactory.php
|
|
|
|
-
|
|
message: '#^Return type \(array\<string, mixed\>\) of method Database\\Factories\\UserFactory\:\:definition\(\) should be compatible with return type \(array\<model property of App\\Models\\User, mixed\>\) of method Illuminate\\Database\\Eloquent\\Factories\\Factory\<App\\Models\\User\>\:\:definition\(\)$#'
|
|
identifier: method.childReturnType
|
|
count: 1
|
|
path: database/factories/UserFactory.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\Mixins\\Expectation\<mixed\>\:\:\$not\.$#'
|
|
identifier: property.notFound
|
|
count: 1
|
|
path: tests/Feature/AdminSystemSettingsTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$adminId\.$#'
|
|
identifier: property.notFound
|
|
count: 11
|
|
path: tests/Feature/AdminSystemSettingsTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:getJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 1
|
|
path: tests/Feature/AdminSystemSettingsTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:putJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 8
|
|
path: tests/Feature/AdminSystemSettingsTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 9
|
|
path: tests/Feature/Auth/AuthControllerTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:getJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 2
|
|
path: tests/Feature/Auth/AuthControllerTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 14
|
|
path: tests/Feature/Auth/AuthControllerTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 5
|
|
path: tests/Feature/Auth/ForgotPasswordTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 9
|
|
path: tests/Feature/Auth/ForgotPasswordTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 7
|
|
path: tests/Feature/Auth/IpLockoutTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 6
|
|
path: tests/Feature/Auth/IpLockoutTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 8
|
|
path: tests/Feature/Auth/RateLimitTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 18
|
|
path: tests/Feature/Auth/RateLimitTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$plainCodes\.$#'
|
|
identifier: property.notFound
|
|
count: 1
|
|
path: tests/Feature/Auth/RecoveryCodeTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 1
|
|
path: tests/Feature/Auth/RecoveryCodeTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$user\.$#'
|
|
identifier: property.notFound
|
|
count: 2
|
|
path: tests/Feature/Auth/RecoveryCodeTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 7
|
|
path: tests/Feature/Auth/RecoveryCodeTest.php
|
|
|
|
-
|
|
message: '#^Parameter \#1 \$self of function startPending expects Tests\\TestCase, Pest\\PendingCalls\\TestCall given\.$#'
|
|
identifier: argument.type
|
|
count: 5
|
|
path: tests/Feature/Auth/RecoveryCodeTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 1
|
|
path: tests/Feature/Auth/ResetPasswordTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$user\.$#'
|
|
identifier: property.notFound
|
|
count: 7
|
|
path: tests/Feature/Auth/ResetPasswordTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 7
|
|
path: tests/Feature/Auth/ResetPasswordTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 1
|
|
path: tests/Feature/Auth/SuspiciousLoginNotificationTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$user\.$#'
|
|
identifier: property.notFound
|
|
count: 1
|
|
path: tests/Feature/Auth/SuspiciousLoginNotificationTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 7
|
|
path: tests/Feature/Auth/SuspiciousLoginNotificationTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 1
|
|
path: tests/Feature/Auth/TwoFactorSetupTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$user\.$#'
|
|
identifier: property.notFound
|
|
count: 16
|
|
path: tests/Feature/Auth/TwoFactorSetupTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:actingAs\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 1
|
|
path: tests/Feature/Auth/TwoFactorSetupTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 16
|
|
path: tests/Feature/Auth/TwoFactorSetupTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$google2fa\.$#'
|
|
identifier: property.notFound
|
|
count: 4
|
|
path: tests/Feature/Auth/TwoFactorTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 1
|
|
path: tests/Feature/Auth/TwoFactorTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$totpSecret\.$#'
|
|
identifier: property.notFound
|
|
count: 4
|
|
path: tests/Feature/Auth/TwoFactorTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$user\.$#'
|
|
identifier: property.notFound
|
|
count: 4
|
|
path: tests/Feature/Auth/TwoFactorTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:getJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 2
|
|
path: tests/Feature/Auth/TwoFactorTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 11
|
|
path: tests/Feature/Auth/TwoFactorTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 30
|
|
path: tests/Feature/DealCreateTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 18
|
|
path: tests/Feature/DealCreateTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$manager\.$#'
|
|
identifier: property.notFound
|
|
count: 5
|
|
path: tests/Feature/DealIndexTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$otherTenant\.$#'
|
|
identifier: property.notFound
|
|
count: 4
|
|
path: tests/Feature/DealIndexTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$project\.$#'
|
|
identifier: property.notFound
|
|
count: 18
|
|
path: tests/Feature/DealIndexTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$project2\.$#'
|
|
identifier: property.notFound
|
|
count: 3
|
|
path: tests/Feature/DealIndexTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 33
|
|
path: tests/Feature/DealIndexTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:getJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 14
|
|
path: tests/Feature/DealIndexTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$otherTenant\.$#'
|
|
identifier: property.notFound
|
|
count: 5
|
|
path: tests/Feature/DealTransitionTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$project\.$#'
|
|
identifier: property.notFound
|
|
count: 5
|
|
path: tests/Feature/DealTransitionTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 12
|
|
path: tests/Feature/DealTransitionTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 7
|
|
path: tests/Feature/DealTransitionTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$adminId\.$#'
|
|
identifier: property.notFound
|
|
count: 15
|
|
path: tests/Feature/ImpersonationTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 15
|
|
path: tests/Feature/ImpersonationTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:getJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 2
|
|
path: tests/Feature/ImpersonationTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 17
|
|
path: tests/Feature/ImpersonationTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 20
|
|
path: tests/Feature/LookupsTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:getJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 5
|
|
path: tests/Feature/LookupsTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 3
|
|
path: tests/Feature/LookupsTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$partitionsBefore\.$#'
|
|
identifier: property.notFound
|
|
count: 2
|
|
path: tests/Feature/PartitionsCreateMonthsTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$project1Id\.$#'
|
|
identifier: property.notFound
|
|
count: 1
|
|
path: tests/Feature/RlsSmokeTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$project2Id\.$#'
|
|
identifier: property.notFound
|
|
count: 1
|
|
path: tests/Feature/RlsSmokeTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant1Id\.$#'
|
|
identifier: property.notFound
|
|
count: 5
|
|
path: tests/Feature/RlsSmokeTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant2Id\.$#'
|
|
identifier: property.notFound
|
|
count: 5
|
|
path: tests/Feature/RlsSmokeTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$project\.$#'
|
|
identifier: property.notFound
|
|
count: 19
|
|
path: tests/Feature/Services/SupplierResolverTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$resolver\.$#'
|
|
identifier: property.notFound
|
|
count: 10
|
|
path: tests/Feature/Services/SupplierResolverTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 2
|
|
path: tests/Feature/Services/SupplierResolverTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:get\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 2
|
|
path: tests/Feature/SetTenantContextTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:withHeaders\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 3
|
|
path: tests/Feature/SetTenantContextTest.php
|
|
|
|
-
|
|
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
|
identifier: property.notFound
|
|
count: 20
|
|
path: tests/Feature/WebhookReceiveTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:call\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 3
|
|
path: tests/Feature/WebhookReceiveTest.php
|
|
|
|
-
|
|
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
|
identifier: method.notFound
|
|
count: 14
|
|
path: tests/Feature/WebhookReceiveTest.php
|
|
|
|
-
|
|
message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertTrue\(\) with true will always evaluate to true\.$#'
|
|
identifier: method.alreadyNarrowedType
|
|
count: 1
|
|
path: tests/Unit/ExampleTest.php
|