2026-05-12 14:38:59 +03:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
use App\Models\Project;
|
|
|
|
|
use App\Models\Tenant;
|
|
|
|
|
use App\Models\User;
|
|
|
|
|
|
|
|
|
|
it('accepts update_regions action with add/remove bitmask', function () {
|
|
|
|
|
$tenant = Tenant::factory()->create();
|
|
|
|
|
$user = User::factory()->for($tenant)->create();
|
|
|
|
|
$p = Project::factory()->for($tenant)->create(['region_mask' => 1]);
|
|
|
|
|
|
|
|
|
|
$this->actingAs($user)
|
|
|
|
|
->postJson('/api/projects/bulk', [
|
|
|
|
|
'action' => 'update_regions',
|
|
|
|
|
'ids' => [$p->id],
|
|
|
|
|
'add' => 6, // биты 2+4 = Северо-Западный + Южный
|
|
|
|
|
'remove' => 1, // бит 1 = Центральный
|
|
|
|
|
])
|
|
|
|
|
->assertOk()
|
|
|
|
|
->assertJsonStructure(['updated', 'skipped', 'warnings']);
|
2026-05-12 14:48:10 +03:00
|
|
|
});
|
2026-05-12 14:38:59 +03:00
|
|
|
|
|
|
|
|
it('rejects unknown action', function () {
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
|
|
|
|
|
$this->actingAs($user)
|
|
|
|
|
->postJson('/api/projects/bulk', [
|
|
|
|
|
'action' => 'nuke_everything',
|
|
|
|
|
'ids' => [1],
|
|
|
|
|
])
|
|
|
|
|
->assertStatus(422)
|
|
|
|
|
->assertJsonValidationErrors(['action']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('rejects update_limit with both delta and replace', function () {
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
|
|
|
|
|
$this->actingAs($user)
|
|
|
|
|
->postJson('/api/projects/bulk', [
|
|
|
|
|
'action' => 'update_limit',
|
|
|
|
|
'ids' => [1],
|
|
|
|
|
'delta' => 50,
|
|
|
|
|
'replace' => 500,
|
|
|
|
|
])
|
|
|
|
|
->assertStatus(422);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('rejects empty ids without scope', function () {
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
|
|
|
|
|
$this->actingAs($user)
|
|
|
|
|
->postJson('/api/projects/bulk', [
|
|
|
|
|
'action' => 'pause',
|
|
|
|
|
])
|
|
|
|
|
->assertStatus(422);
|
|
|
|
|
});
|
2026-05-12 14:43:45 +03:00
|
|
|
|
|
|
|
|
it('accepts empty scope.filter as valid scope (all projects)', function () {
|
|
|
|
|
$tenant = Tenant::factory()->create();
|
|
|
|
|
$user = User::factory()->for($tenant)->create();
|
|
|
|
|
|
|
|
|
|
$this->actingAs($user)
|
|
|
|
|
->postJson('/api/projects/bulk', [
|
|
|
|
|
'action' => 'pause',
|
|
|
|
|
'scope' => ['filter' => []],
|
|
|
|
|
])
|
|
|
|
|
->assertOk();
|
|
|
|
|
});
|
2026-05-12 14:48:10 +03:00
|
|
|
|
|
|
|
|
it('applies update_regions add and remove correctly', function () {
|
|
|
|
|
$tenant = Tenant::factory()->create();
|
|
|
|
|
$user = User::factory()->for($tenant)->create();
|
|
|
|
|
$p1 = Project::factory()->for($tenant)->create(['region_mask' => 3]); // 1+2
|
|
|
|
|
$p2 = Project::factory()->for($tenant)->create(['region_mask' => 5]); // 1+4
|
|
|
|
|
|
|
|
|
|
$this->actingAs($user)
|
|
|
|
|
->postJson('/api/projects/bulk', [
|
|
|
|
|
'action' => 'update_regions',
|
|
|
|
|
'ids' => [$p1->id, $p2->id],
|
|
|
|
|
'add' => 16, // 16 = Приволжский
|
|
|
|
|
'remove' => 1, // 1 = Центральный
|
|
|
|
|
])
|
|
|
|
|
->assertOk()
|
|
|
|
|
->assertJson(['updated' => 2, 'skipped' => [], 'warnings' => []]);
|
|
|
|
|
|
|
|
|
|
expect($p1->fresh()->region_mask)->toBe((3 | 16) & ~1); // = 18
|
|
|
|
|
expect($p2->fresh()->region_mask)->toBe((5 | 16) & ~1); // = 20
|
|
|
|
|
});
|
2026-05-12 14:52:23 +03:00
|
|
|
|
|
|
|
|
it('applies update_days add and remove correctly', function () {
|
|
|
|
|
$tenant = Tenant::factory()->create();
|
|
|
|
|
$user = User::factory()->for($tenant)->create();
|
|
|
|
|
$p = Project::factory()->for($tenant)->create(['delivery_days_mask' => 31]); // Пн-Пт
|
|
|
|
|
|
|
|
|
|
$this->actingAs($user)
|
|
|
|
|
->postJson('/api/projects/bulk', [
|
|
|
|
|
'action' => 'update_days',
|
|
|
|
|
'ids' => [$p->id],
|
|
|
|
|
'add' => 96, // 32+64 = Сб+Вс
|
|
|
|
|
'remove' => 1, // Пн
|
|
|
|
|
])
|
|
|
|
|
->assertOk()
|
|
|
|
|
->assertJson(['updated' => 1, 'skipped' => [], 'warnings' => []]);
|
|
|
|
|
|
|
|
|
|
expect($p->fresh()->delivery_days_mask)->toBe((31 | 96) & ~1); // = 126
|
|
|
|
|
});
|