Merge pull request #17287 from uberbrady/limit_license_seat_increments

Fixed [FD-45786] - Limit changing of asset seat count to no more than 10k at a time
This commit is contained in:
snipe
2025-06-24 17:43:02 +01:00
committed by GitHub
3 changed files with 141 additions and 10 deletions
+12 -8
View File
@@ -43,7 +43,7 @@ class License extends Depreciable
protected $rules = [
'name' => 'required|string|min:3|max:255',
'seats' => 'required|min:1|integer',
'seats' => 'required|min:1|integer|limit_change:10000', // limit_change is a "pseudo-rule" that translates into 'between', see prepareLimitChangeRule() below
'license_email' => 'email|nullable|max:120',
'license_name' => 'string|nullable|max:100',
'notes' => 'string|nullable',
@@ -148,6 +148,14 @@ class License extends Depreciable
});
}
public function prepareLimitChangeRule($parameters, $field)
{
$actual_seat_count = $this->licenseseats()->count(); //we use the *actual* seat count here, in case your license has gone wonky
$lower_bound = $actual_seat_count - $parameters[0];
$upper_bound = $actual_seat_count + $parameters[0];
return ["between", ($lower_bound <= 0 ? 1 : $lower_bound), $upper_bound];
}
/**
* Balance seat counts
*
@@ -164,21 +172,17 @@ class License extends Depreciable
// On Create, we just make one for each of the seats.
$change = abs($oldSeats - $newSeats);
if ($oldSeats > $newSeats) {
$license->load('licenseseats.user');
// Need to delete seats... lets see if if we have enough.
$seatsAvailableForDelete = $license->licenseseats->reject(function ($seat) {
return ((bool) $seat->assigned_to) || ((bool) $seat->asset_id);
});
$seatsAvailableForDelete = $license->licenseseats()->whereNull('assigned_to')->whereNull('asset_id')->limit($change);
if ($change > $seatsAvailableForDelete->count()) {
Session::flash('error', trans('admin/licenses/message.assoc_users'));
return false;
}
for ($i = 1; $i <= $change; $i++) {
$seatsAvailableForDelete->pop()->delete();
}
$seatsAvailableForDelete->delete();
// Log Deletion of seats.
$logAction = new Actionlog;
$logAction->item_type = self::class;
@@ -2,7 +2,6 @@
namespace Tests\Feature\Licenses\Ui;
use App\Models\AssetModel;
use App\Models\Category;
use App\Models\License;
use App\Models\Depreciation;
@@ -41,7 +40,48 @@ class CreateLicenseTest extends TestCase
$response->assertInvalid(['purchase_date']);
$response->assertSessionHasErrors(['purchase_date']);
$this->followRedirects($response)->assertSee(trans('general.error'));
$this->assertFalse(AssetModel::where('name', 'Test Invalid License')->exists());
$this->assertFalse(License::where('name', 'Test Invalid License')->exists());
}
public function testLicenseCreate()
{
$response = $this->actingAs(User::factory()->superuser()->create())
->from(route('licenses.create'))
->post(route('licenses.store'), [
'name' => 'Test Valid License',
'seats' => '10',
'category_id' => Category::factory()->forLicenses()->create()->id,
]);
$response->assertStatus(302);
$license = License::where('name', 'Test Valid License')->sole();
$this->assertNotNull($license);
//$license->assetlog()->has_one_of_();
$this->assertDatabaseHas('action_logs', ['action_type' => 'create', 'item_id' => $license->id, 'item_type' => License::class]);
$this->assertDatabaseHas('action_logs', ['action_type' => 'add seats', 'item_id' => $license->id, 'item_type' => License::class]);
$this->assertEquals($license->licenseseats()->count(), 10);
//test log entries? Sure.
}
public function testTooManySeatsLicenseCreate()
{
$response = $this->actingAs(User::factory()->superuser()->create())
->from(route('licenses.create'))
->post(route('licenses.store'), [
'name' => 'Test Valid License',
'seats' => '100000',
'category_id' => Category::factory()->forLicenses()->create()->id,
]);
$response->assertStatus(302);
$license = License::where('name', 'Test Valid License')->first();
$this->assertNull($license);
//$license->assetlog()->has_one_of_();
// $this->assertDatabaseMissing('action_logs', ['action_type' => 'create', 'item_id' => $license->id, 'item_type' => License::class]);
// $this->assertDatabaseMissing('action_logs', ['action_type' => 'add seats', 'item_id' => $license->id, 'item_type' => License::class]);
//test log entries? Sure.
}
}
@@ -2,6 +2,7 @@
namespace Tests\Feature\Licenses\Ui;
use App\Models\Category;
use App\Models\License;
use App\Models\User;
use Tests\TestCase;
@@ -14,4 +15,90 @@ class UpdateLicenseTest extends TestCase
->get(route('licenses.edit', License::factory()->create()->id))
->assertOk();
}
public function testCanUpdateLicenseSeats()
{
$admin = User::factory()->superuser()->create();
$license_category = Category::factory()->forLicenses()->create()->id;
$response = $this->actingAs($admin)
->from(route('licenses.create'))
->post(route('licenses.store'), [
'name' => 'Test Update License',
'seats' => '9999',
'category_id' => $license_category,
]);
$response->assertStatus(302);
$license = License::where('name', 'Test Update License')->sole();
$this->assertNotNull($license);
$this->actingAs($admin)
->put(route('licenses.update', $license->id), [
'name' => 'Test Update License',
'seats' => '19999',
'category_id' => $license_category,
])
->assertStatus(302);
$license->refresh();
$this->assertEquals($license->licenseseats()->count(), $license->seats);
$this->assertEquals($license->licenseseats()->count(), 19999);
}
public function testCannotUpdateLicenseSeatsTooMuch()
{
$admin = User::factory()->superuser()->create();
$license_category = Category::factory()->forLicenses()->create()->id;
$response = $this->actingAs($admin)
->from(route('licenses.create'))
->post(route('licenses.store'), [
'name' => 'Test Update License',
'seats' => '9999',
'category_id' => $license_category,
]);
$response->assertStatus(302);
$license = License::where('name', 'Test Update License')->sole();
$this->assertNotNull($license);
$this->actingAs($admin)
->put(route('licenses.update', $license->id), [
'name' => 'Test Update License',
'seats' => '29999',
'category_id' => $license_category,
])
->assertStatus(302);
$license->refresh();
$this->assertEquals($license->licenseseats()->count(), $license->seats);
$this->assertEquals($license->licenseseats()->count(), 9999);
}
public function testCanRemoveLicenseSeats()
{
$admin = User::factory()->superuser()->create();
$license_category = Category::factory()->forLicenses()->create()->id;
$response = $this->actingAs($admin)
->from(route('licenses.create'))
->post(route('licenses.store'), [
'name' => 'Test Remove License Seats',
'seats' => '9999',
'category_id' => $license_category,
]);
$response->assertStatus(302);
$license = License::where('name', 'Test Remove License Seats')->sole();
$this->assertNotNull($license);
$this->actingAs($admin)
->put(route('licenses.update', $license->id), [
'name' => 'Test Remove License Seats',
'seats' => '5000',
'category_id' => $license_category,
])
->assertStatus(302);
$license->refresh();
$this->assertEquals($license->licenseseats()->count(), $license->seats);
$this->assertEquals($license->licenseseats()->count(), 5000);
}
}