Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #15005 - Improvements on user merge #15016

Merged
merged 19 commits into from
Jul 3, 2024
Merged
19 changes: 18 additions & 1 deletion app/Console/Commands/MergeUsersByUsername.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Console\Commands;

use App\Events\UserMerged;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
Expand Down Expand Up @@ -51,7 +52,7 @@ public function handle()

$bad_users = User::where('username', '=', trim($parts[0]))
->whereNull('deleted_at')
->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations')
->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations','uploads', 'acceptances')
->get();


Expand Down Expand Up @@ -105,10 +106,26 @@ public function handle()
$managedLocation->save();
}

foreach ($bad_user->uploads as $upload) {
$this->info('Updating upload log record '.$upload->id.' to user '.$user->id);
$upload->item_id = $user->id;
$upload->save();
}

foreach ($bad_user->acceptances as $acceptance) {
$this->info('Updating acceptance log record '.$acceptance->id.' to user '.$user->id);
$acceptance->item_id = $user->id;
$acceptance->save();
}

// Mark the user as deleted
$this->info('Marking the user as deleted');
$bad_user->deleted_at = Carbon::now()->timestamp;
$bad_user->save();

event(new UserMerged($bad_user, $user, null));


}
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/Events/UserMerged.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class UserMerged
*
* @return void
*/
public function __construct(User $from_user, User $to_user, User $admin)
public function __construct(User $from_user, User $to_user, ?User $admin)
{
$this->merged_from = $from_user;
$this->merged_to = $to_user;
Expand Down
15 changes: 12 additions & 3 deletions app/Http/Controllers/Users/BulkUsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ public function merge(Request $request)

// Get the users
$merge_into_user = User::find($request->input('merge_into_id'));
$users_to_merge = User::whereIn('id', $user_ids_to_merge)->with('assets', 'licenses', 'consumables','accessories')->get();
$users_to_merge = User::whereIn('id', $user_ids_to_merge)->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations','uploads', 'acceptances')->get();
$admin = User::find(Auth::user()->id);

// Walk users
Expand All @@ -344,10 +344,20 @@ public function merge(Request $request)
}

foreach ($user_to_merge->userlog as $log) {
$log->target_id = $user_to_merge->id;
$log->target_id = $merge_into_user->id;
$log->save();
}

foreach ($user_to_merge->uploads as $upload) {
$upload->item_id = $merge_into_user->id;
$upload->save();
}

foreach ($user_to_merge->acceptances as $acceptance) {
$acceptance->item_id = $merge_into_user->id;
$acceptance->save();
}

User::where('manager_id', '=', $user_to_merge->id)->update(['manager_id' => $merge_into_user->id]);

foreach ($user_to_merge->managedLocations as $managedLocation) {
Expand All @@ -356,7 +366,6 @@ public function merge(Request $request)
}

$user_to_merge->delete();
//$user_to_merge->save();

event(new UserMerged($user_to_merge, $merge_into_user, $admin));

Expand Down
4 changes: 2 additions & 2 deletions app/Listeners/LogListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public function onUserMerged(UserMerged $event)
$logaction->target_type = User::class;
$logaction->action_type = 'merged';
$logaction->note = trans('general.merged_log_this_user_from', $to_from_array);
$logaction->user_id = $event->admin->id;
$logaction->user_id = $event->admin->id ?? null;
$logaction->save();

// Add a record to the users being merged TO
Expand All @@ -122,7 +122,7 @@ public function onUserMerged(UserMerged $event)
$logaction->item_type = User::class;
$logaction->action_type = 'merged';
$logaction->note = trans('general.merged_log_this_user_into', $to_from_array);
$logaction->user_id = $event->admin->id;
$logaction->user_id = $event->admin->id ?? null;
$logaction->save();


Expand Down
17 changes: 15 additions & 2 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,6 @@ public function assetlog()
/**
* Establishes the user -> uploads relationship
*
* @todo I don't think we use this?
*
* @author A. Gianotto <[email protected]>
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
Expand All @@ -496,6 +494,21 @@ public function uploads()
->orderBy('created_at', 'desc');
}

/**
* Establishes the user -> acceptances relationship
*
* @author A. Gianotto <[email protected]>
* @since [v7.0.7]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function acceptances()
{
return $this->hasMany(\App\Models\Actionlog::class, 'target_id')
->where('target_type', self::class)
->where('action_type', '=', 'accepted')
->orderBy('created_at', 'desc');
}

/**
* Establishes the user -> requested assets relationship
*
Expand Down
60 changes: 60 additions & 0 deletions database/factories/ActionlogFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,64 @@ public function licenseCheckoutToUser()
];
});
}

public function filesUploaded()
{
return $this->state(function () {

return [
'created_at' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()),
'action_type' => 'uploaded',
'item_type' => User::class,
'filename' => $this->faker->unixTime('now'),
];
});
}

public function acceptedSignature()
{
return $this->state(function () {

$asset = Asset::factory()->create();

return [
'created_at' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()),
'action_type' => 'accepted',
'item_id' => $asset->id,
'item_type' => Asset::class,
'target_type' => User::class,
'accept_signature' => $this->faker->unixTime('now'),
];
});
}

public function acceptedEula()
{
return $this->state(function () {

$asset = Asset::factory()->create();

return [
'created_at' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()),
'action_type' => 'accepted',
'item_id' => $asset->id,
'item_type' => Asset::class,
'target_type' => User::class,
'filename' => $this->faker->unixTime('now'),
];
});
}

public function userUpdated()
{
return $this->state(function () {

return [
'created_at' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()),
'action_type' => 'update',
'target_type' => User::class,
'item_type' => User::class,
];
});
}
}
13 changes: 13 additions & 0 deletions database/factories/ConsumableFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Models\Consumable;
use App\Models\Manufacturer;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Supplier;

Expand Down Expand Up @@ -116,4 +117,16 @@ public function requiringAcceptance()
$consumable->category->update(['require_acceptance' => 1]);
});
}

public function checkedOutToUser(User $user = null)
{
return $this->afterCreating(function (Consumable $consumable) use ($user) {
$consumable->users()->attach($consumable->id, [
'consumable_id' => $consumable->id,
'created_at' => Carbon::now(),
'user_id' => User::factory()->create()->id,
'assigned_to' => $user->id ?? User::factory()->create()->id,
]);
});
}
}
135 changes: 135 additions & 0 deletions tests/Feature/Console/MergeUsersTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php

namespace Tests\Feature\Console;

use App\Models\Accessory;
use App\Models\Asset;
use App\Models\Consumable;
use App\Models\LicenseSeat;
use App\Models\User;
use App\Models\Actionlog;
use Tests\TestCase;


class MergeUsersTest extends TestCase
{
public function testAssetsAreTransferredOnUserMerge()
{
$user1 = User::factory()->create(['username' => 'user1']);
$user_to_merge_into = User::factory()->create(['username' => '[email protected]']);

Asset::factory()->count(3)->assignedToUser($user1)->create();
Asset::factory()->count(3)->assignedToUser($user_to_merge_into)->create();

$this->artisan('snipeit:merge-users')->assertExitCode(0);

$this->assertEquals(6, $user_to_merge_into->refresh()->assets->count());
$this->assertEquals(0, $user1->refresh()->assets->count());

}

public function testLicensesAreTransferredOnUserMerge(): void
{
$user1 = User::factory()->create(['username' => 'user1']);
$user_to_merge_into = User::factory()->create(['username' => '[email protected]']);

LicenseSeat::factory()->count(3)->create(['assigned_to' => $user1->id]);
LicenseSeat::factory()->count(3)->create(['assigned_to' => $user_to_merge_into->id]);

$this->assertEquals(3, $user_to_merge_into->refresh()->licenses->count());

$this->artisan('snipeit:merge-users')->assertExitCode(0);

$this->assertEquals(6, $user_to_merge_into->refresh()->licenses->count());
$this->assertEquals(0, $user1->refresh()->licenses->count());

}

public function testAccessoriesTransferredOnUserMerge(): void
{
$user1 = User::factory()->create(['username' => 'user1']);
$user_to_merge_into = User::factory()->create(['username' => '[email protected]']);

Accessory::factory()->count(3)->checkedOutToUser($user1)->create();
Accessory::factory()->count(3)->checkedOutToUser($user_to_merge_into)->create();

$this->assertEquals(3, $user_to_merge_into->refresh()->accessories->count());

$this->artisan('snipeit:merge-users')->assertExitCode(0);

$this->assertEquals(6, $user_to_merge_into->refresh()->accessories->count());
$this->assertEquals(0, $user1->refresh()->accessories->count());

}

public function testConsumablesTransferredOnUserMerge(): void
{
$user1 = User::factory()->create(['username' => 'user1']);
$user_to_merge_into = User::factory()->create(['username' => '[email protected]']);

Consumable::factory()->count(3)->checkedOutToUser($user1)->create();
Consumable::factory()->count(3)->checkedOutToUser($user_to_merge_into)->create();

$this->assertEquals(3, $user_to_merge_into->refresh()->consumables->count());

$this->artisan('snipeit:merge-users')->assertExitCode(0);

$this->assertEquals(6, $user_to_merge_into->refresh()->consumables->count());
$this->assertEquals(0, $user1->refresh()->consumables->count());

}

public function testFilesAreTransferredOnUserMerge(): void
{
$user1 = User::factory()->create(['username' => 'user1']);
$user_to_merge_into = User::factory()->create(['username' => '[email protected]']);

Actionlog::factory()->count(3)->filesUploaded()->create(['item_id' => $user1->id]);
Actionlog::factory()->count(3)->filesUploaded()->create(['item_id' => $user_to_merge_into->id]);

$this->assertEquals(3, $user_to_merge_into->refresh()->uploads->count());

$this->artisan('snipeit:merge-users')->assertExitCode(0);

$this->assertEquals(6, $user_to_merge_into->refresh()->uploads->count());
$this->assertEquals(0, $user1->refresh()->uploads->count());

}

public function testAcceptancesAreTransferredOnUserMerge(): void
{
$user1 = User::factory()->create(['username' => 'user1']);
$user_to_merge_into = User::factory()->create(['username' => '[email protected]']);

Actionlog::factory()->count(3)->acceptedSignature()->create(['target_id' => $user1->id]);
Actionlog::factory()->count(3)->acceptedSignature()->create(['target_id' => $user_to_merge_into->id]);

$this->assertEquals(3, $user_to_merge_into->refresh()->acceptances->count());

$this->artisan('snipeit:merge-users')->assertExitCode(0);

$this->assertEquals(6, $user_to_merge_into->refresh()->acceptances->count());
$this->assertEquals(0, $user1->refresh()->acceptances->count());

}

public function testUserUpdateHistoryIsTransferredOnUserMerge(): void
{
$user1 = User::factory()->create(['username' => 'user1']);
$user_to_merge_into = User::factory()->create(['username' => '[email protected]']);

Actionlog::factory()->count(3)->userUpdated()->create(['target_id' => $user1->id, 'item_id' => $user1->id]);
Actionlog::factory()->count(3)->userUpdated()->create(['target_id' => $user_to_merge_into->id, 'item_id' => $user_to_merge_into->id]);

$this->assertEquals(3, $user_to_merge_into->refresh()->userlog->count());

$this->artisan('snipeit:merge-users')->assertExitCode(0);

// This needs to be more than the otherwise expected because the merge action itself is logged for the two merging users
$this->assertEquals(7, $user_to_merge_into->refresh()->userlog->count());
$this->assertEquals(1, $user1->refresh()->userlog->count());

}


}
Loading
Loading