mirror of
https://github.com/snipe/snipe-it.git
synced 2026-02-04 23:05:41 +00:00
Merge pull request #18359 from marcusmoore/17816-qty-in-activity-report
Fixed #17816 - added quantity to activity reports
This commit is contained in:
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\ActionType;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MigrateLicenseSeatQuantitiesInActionLogs extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:migrate-license-seat-quantities-in-action-logs
|
||||
{--no-interaction: Do not ask any interactive question}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Updates quantity field in action_logs table for license seats that were added or deleted.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$query = DB::table('action_logs')
|
||||
->whereIn('action_type', [
|
||||
ActionType::AddSeats->value,
|
||||
ActionType::DeleteSeats->value,
|
||||
])
|
||||
->where('quantity', '=', 1)
|
||||
->orderBy('id');
|
||||
|
||||
$count = $query->count();
|
||||
|
||||
if ($count === 0) {
|
||||
$this->info('Nothing to update');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->info("{$count} logs to update");
|
||||
|
||||
if ($this->option('no-interaction') || $this->confirm('Update quantities in the action log?')) {
|
||||
$query->chunk(50, function ($logs) {
|
||||
$logs->each(function ($log) {
|
||||
$quantityFromNote = Str::between($log->note, "ed ", " seats");
|
||||
|
||||
if (!is_numeric($quantityFromNote)) {
|
||||
$this->error('Could not parse quantity from ID: {id}', ['id' => $log->id]);
|
||||
}
|
||||
|
||||
if ($log->quantity !== (int) $quantityFromNote) {
|
||||
$this->info(vsprintf('Updating id: %s to quantity %s', [
|
||||
'id' => $log->id,
|
||||
'new_quantity' => $quantityFromNote,
|
||||
]));
|
||||
|
||||
DB::table('action_logs')->where('id', $log->id)->update(['quantity' => (int) $quantityFromNote]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -15,18 +15,20 @@ class CheckoutableCheckedOut
|
||||
public $checkedOutBy;
|
||||
public $note;
|
||||
public $originalValues;
|
||||
public int $quantity;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($checkoutable, $checkedOutTo, User $checkedOutBy, $note, $originalValues = [])
|
||||
public function __construct($checkoutable, $checkedOutTo, User $checkedOutBy, $note, $originalValues = [], $quantity = 1)
|
||||
{
|
||||
$this->checkoutable = $checkoutable;
|
||||
$this->checkedOutTo = $checkedOutTo;
|
||||
$this->checkedOutBy = $checkedOutBy;
|
||||
$this->note = $note;
|
||||
$this->originalValues = $originalValues;
|
||||
$this->quantity = $quantity;
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ class AccessoryCheckoutController extends Controller
|
||||
*/
|
||||
public function store(AccessoryCheckoutRequest $request, Accessory $accessory) : RedirectResponse
|
||||
{
|
||||
|
||||
|
||||
$this->authorize('checkout', $accessory);
|
||||
|
||||
$target = $this->determineCheckoutTarget();
|
||||
@ -89,7 +89,14 @@ class AccessoryCheckoutController extends Controller
|
||||
$accessory_checkout->save();
|
||||
}
|
||||
|
||||
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
|
||||
event(new CheckoutableCheckedOut(
|
||||
$accessory,
|
||||
$target,
|
||||
auth()->user(),
|
||||
$request->input('note'),
|
||||
[],
|
||||
$accessory->checkout_qty,
|
||||
));
|
||||
|
||||
$request->request->add(['checkout_to_type' => request('checkout_to_type')]);
|
||||
$request->request->add(['assigned_to' => $target->id]);
|
||||
|
||||
@ -325,7 +325,14 @@ class AccessoriesController extends Controller
|
||||
}
|
||||
|
||||
// Set this value to be able to pass the qty through to the event
|
||||
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
|
||||
event(new CheckoutableCheckedOut(
|
||||
$accessory,
|
||||
$target,
|
||||
auth()->user(),
|
||||
$request->input('note'),
|
||||
[],
|
||||
$accessory->checkout_qty,
|
||||
));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $payload, trans('admin/accessories/message.checkout.success')));
|
||||
|
||||
|
||||
@ -321,7 +321,7 @@ class ComponentsController extends Controller
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
|
||||
$component->logCheckout($request->input('note'), $asset);
|
||||
$component->logCheckout($request->input('note'), $asset, null, [], $request->get('assigned_qty', 1));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.checkout.success')));
|
||||
}
|
||||
|
||||
@ -326,8 +326,14 @@ class ConsumablesController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note')));
|
||||
event(new CheckoutableCheckedOut(
|
||||
$consumable,
|
||||
$user,
|
||||
auth()->user(),
|
||||
$request->input('note'),
|
||||
[],
|
||||
$consumable->checkout_qty,
|
||||
));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.checkout.success')));
|
||||
|
||||
|
||||
@ -115,7 +115,14 @@ class ComponentCheckoutController extends Controller
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
|
||||
event(new CheckoutableCheckedOut($component, $asset, auth()->user(), $request->input('note')));
|
||||
event(new CheckoutableCheckedOut(
|
||||
$component,
|
||||
$asset,
|
||||
auth()->user(),
|
||||
$request->input('note'),
|
||||
[],
|
||||
$component->checkout_qty,
|
||||
));
|
||||
|
||||
$request->request->add(['checkout_to_type' => 'asset']);
|
||||
$request->request->add(['assigned_asset' => $asset->id]);
|
||||
|
||||
@ -102,7 +102,15 @@ class ConsumableCheckoutController extends Controller
|
||||
}
|
||||
|
||||
$consumable->checkout_qty = $quantity;
|
||||
event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note')));
|
||||
|
||||
event(new CheckoutableCheckedOut(
|
||||
$consumable,
|
||||
$user,
|
||||
auth()->user(),
|
||||
$request->input('note'),
|
||||
[],
|
||||
$consumable->checkout_qty,
|
||||
));
|
||||
|
||||
$request->request->add(['checkout_to_type' => 'user']);
|
||||
$request->request->add(['assigned_user' => $user->id]);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Enums\ActionType;
|
||||
use App\Helpers\Helper;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Models\Actionlog;
|
||||
@ -80,7 +81,7 @@ class ActionlogsTransformer
|
||||
|
||||
// this is a custom field
|
||||
if (str_starts_with($fieldname, '_snipeit_')) {
|
||||
|
||||
|
||||
foreach ($custom_fields as $custom_field) {
|
||||
|
||||
if ($custom_field->db_column == $fieldname) {
|
||||
@ -185,7 +186,7 @@ class ActionlogsTransformer
|
||||
'name' => e($actionlog->target->display_name) ?? null,
|
||||
'type' => e($actionlog->targetType()),
|
||||
] : null,
|
||||
|
||||
'quantity' => $this->getQuantity($actionlog),
|
||||
'note' => ($actionlog->note) ? Helper::parseEscapedMarkedownInline($actionlog->note): null,
|
||||
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
|
||||
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
|
||||
@ -336,6 +337,26 @@ class ActionlogsTransformer
|
||||
|
||||
}
|
||||
|
||||
private function getQuantity(Actionlog $actionlog): ?int
|
||||
{
|
||||
if (!$actionlog->quantity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// only a few action types will have a quantity we are interested in.
|
||||
if (!in_array($actionlog->action_type, [
|
||||
ActionType::Checkout->value,
|
||||
ActionType::Accepted->value,
|
||||
ActionType::Declined->value,
|
||||
ActionType::CheckinFrom->value,
|
||||
ActionType::AddSeats->value,
|
||||
ActionType::DeleteSeats->value,
|
||||
])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $actionlog->quantity;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -47,7 +47,13 @@ class LogListener
|
||||
*/
|
||||
public function onCheckoutableCheckedOut(CheckoutableCheckedOut $event)
|
||||
{
|
||||
$event->checkoutable->logCheckout($event->note, $event->checkedOutTo, $event->checkoutable->last_checkout, $event->originalValues);
|
||||
$event->checkoutable->logCheckout(
|
||||
$event->note,
|
||||
$event->checkedOutTo,
|
||||
$event->checkoutable->last_checkout,
|
||||
$event->originalValues,
|
||||
$event->quantity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,6 +72,7 @@ class LogListener
|
||||
$logaction->note = $event->acceptance->note;
|
||||
$logaction->action_type = 'accepted';
|
||||
$logaction->action_date = $event->acceptance->accepted_at;
|
||||
$logaction->quantity = $event->acceptance->qty ?? 1;
|
||||
|
||||
// TODO: log the actual license seat that was checked out
|
||||
if ($event->acceptance->checkoutable instanceof LicenseSeat) {
|
||||
@ -84,6 +91,7 @@ class LogListener
|
||||
$logaction->note = $event->acceptance->note;
|
||||
$logaction->action_type = 'declined';
|
||||
$logaction->action_date = $event->acceptance->declined_at;
|
||||
$logaction->quantity = $event->acceptance->qty ?? 1;
|
||||
|
||||
// TODO: log the actual license seat that was checked out
|
||||
if ($event->acceptance->checkoutable instanceof LicenseSeat) {
|
||||
|
||||
@ -223,6 +223,7 @@ class License extends Depreciable
|
||||
$logAction->created_by = auth()->id() ?: 1; // We don't have an id while running the importer from CLI.
|
||||
$logAction->note = "deleted {$change} seats";
|
||||
$logAction->target_id = null;
|
||||
$logAction->quantity = $change;
|
||||
$logAction->logaction('delete seats');
|
||||
|
||||
return true;
|
||||
@ -259,6 +260,7 @@ class License extends Depreciable
|
||||
$logAction->created_by = auth()->id() ?: 1; // Importer.
|
||||
$logAction->note = "added {$change} seats";
|
||||
$logAction->target_id = null;
|
||||
$logAction->quantity = $change;
|
||||
$logAction->logaction('add seats');
|
||||
}
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ trait Loggable
|
||||
* @since [v3.4]
|
||||
* @return \App\Models\Actionlog
|
||||
*/
|
||||
public function logCheckout($note, $target, $action_date = null, $originalValues = [])
|
||||
public function logCheckout($note, $target, $action_date = null, $originalValues = [], $quantity = 1)
|
||||
{
|
||||
|
||||
$log = new Actionlog;
|
||||
@ -94,7 +94,7 @@ trait Loggable
|
||||
|
||||
$log->note = $note;
|
||||
$log->action_date = $action_date;
|
||||
|
||||
$log->quantity = $quantity;
|
||||
|
||||
$changed = [];
|
||||
$array_to_flip = array_keys($fields_array);
|
||||
|
||||
@ -117,6 +117,13 @@ class HistoryPresenter extends Presenter
|
||||
'visible' => true,
|
||||
'formatter' => 'fileDownloadButtonsFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'quantity',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'visible' => true,
|
||||
'title' => trans('general.quantity'),
|
||||
],
|
||||
[
|
||||
'field' => 'note',
|
||||
'searchable' => true,
|
||||
@ -169,4 +176,4 @@ class HistoryPresenter extends Presenter
|
||||
return json_encode($merged);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('action_logs', function (Blueprint $table) {
|
||||
$table->integer('quantity')->default(1)->after('expected_checkin');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('action_logs', function (Blueprint $table) {
|
||||
$table->dropColumn('quantity');
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -14,6 +14,72 @@ use Tests\TestCase;
|
||||
|
||||
class AccessoryAcceptanceTest extends TestCase
|
||||
{
|
||||
public function test_can_accept_accessory_checkout()
|
||||
{
|
||||
$assignee = User::factory()->create();
|
||||
$accessory = Accessory::factory()->create();
|
||||
|
||||
$checkoutAcceptance = CheckoutAcceptance::factory()
|
||||
->pending()
|
||||
->for($assignee, 'assignedTo')
|
||||
->for($accessory, 'checkoutable')
|
||||
->create(['qty' => 2]);
|
||||
|
||||
$this->actingAs($assignee)
|
||||
->post(route('account.store-acceptance', $checkoutAcceptance), [
|
||||
'asset_acceptance' => 'accepted',
|
||||
'note' => 'A note here',
|
||||
])
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertNotNull($checkoutAcceptance->refresh()->accepted_at);
|
||||
$this->assertEquals('A note here', $checkoutAcceptance->note);
|
||||
|
||||
$assignee->accessories->contains($accessory);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'action_type' => 'accepted',
|
||||
'target_id' => $assignee->id,
|
||||
'target_type' => User::class,
|
||||
'item_id' => $accessory->id,
|
||||
'item_type' => Accessory::class,
|
||||
'quantity' => 2,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_decline_accessory_checkout()
|
||||
{
|
||||
$assignee = User::factory()->create();
|
||||
$accessory = Accessory::factory()->create();
|
||||
|
||||
$checkoutAcceptance = CheckoutAcceptance::factory()
|
||||
->pending()
|
||||
->for($assignee, 'assignedTo')
|
||||
->for($accessory, 'checkoutable')
|
||||
->create(['qty' => 2]);
|
||||
|
||||
$this->actingAs($assignee)
|
||||
->post(route('account.store-acceptance', $checkoutAcceptance), [
|
||||
'asset_acceptance' => 'declined',
|
||||
'note' => 'A note here',
|
||||
])
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertNotNull($checkoutAcceptance->refresh()->declined_at);
|
||||
$this->assertEquals('A note here', $checkoutAcceptance->note);
|
||||
|
||||
$assignee->accessories->doesntContain($accessory);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'action_type' => 'declined',
|
||||
'target_id' => $assignee->id,
|
||||
'target_type' => User::class,
|
||||
'item_id' => $accessory->id,
|
||||
'item_type' => Accessory::class,
|
||||
'quantity' => 2,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be absorbed into a bigger test
|
||||
*/
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\CheckoutAcceptances\Ui;
|
||||
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\User;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ConsumableAcceptanceTest extends TestCase
|
||||
{
|
||||
public function test_can_accept_consumable_checkout()
|
||||
{
|
||||
$assignee = User::factory()->create();
|
||||
$consumable = Consumable::factory()->create();
|
||||
|
||||
$checkoutAcceptance = CheckoutAcceptance::factory()
|
||||
->pending()
|
||||
->for($assignee, 'assignedTo')
|
||||
->for($consumable, 'checkoutable')
|
||||
->create(['qty' => 2]);
|
||||
|
||||
$this->actingAs($assignee)
|
||||
->post(route('account.store-acceptance', $checkoutAcceptance), [
|
||||
'asset_acceptance' => 'accepted',
|
||||
'note' => 'A note here',
|
||||
])
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertNotNull($checkoutAcceptance->refresh()->accepted_at);
|
||||
$this->assertEquals('A note here', $checkoutAcceptance->note);
|
||||
|
||||
$assignee->consumables->contains($consumable);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'action_type' => 'accepted',
|
||||
'target_id' => $assignee->id,
|
||||
'target_type' => User::class,
|
||||
'item_id' => $consumable->id,
|
||||
'item_type' => Consumable::class,
|
||||
'quantity' => 2,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_decline_consumable_checkout()
|
||||
{
|
||||
$assignee = User::factory()->create();
|
||||
$consumable = Consumable::factory()->create();
|
||||
|
||||
$checkoutAcceptance = CheckoutAcceptance::factory()
|
||||
->pending()
|
||||
->for($assignee, 'assignedTo')
|
||||
->for($consumable, 'checkoutable')
|
||||
->create(['qty' => 2]);
|
||||
|
||||
$this->actingAs($assignee)
|
||||
->post(route('account.store-acceptance', $checkoutAcceptance), [
|
||||
'asset_acceptance' => 'declined',
|
||||
'note' => 'A note here',
|
||||
])
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertNotNull($checkoutAcceptance->refresh()->declined_at);
|
||||
$this->assertEquals('A note here', $checkoutAcceptance->note);
|
||||
|
||||
$assignee->consumables->doesntContain($consumable);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'action_type' => 'declined',
|
||||
'target_id' => $assignee->id,
|
||||
'target_type' => User::class,
|
||||
'item_id' => $consumable->id,
|
||||
'item_type' => Consumable::class,
|
||||
'quantity' => 2,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -115,20 +115,17 @@ class AccessoryCheckoutTest extends TestCase implements TestsPermissionsRequirem
|
||||
|
||||
$this->assertTrue($accessory->checkouts()->where('assigned_type', User::class)->where('assigned_to', $user->id)->count() > 0);
|
||||
|
||||
$this->assertEquals(
|
||||
1,
|
||||
Actionlog::where([
|
||||
'action_type' => 'checkout',
|
||||
'target_id' => $user->id,
|
||||
'target_type' => User::class,
|
||||
'item_id' => $accessory->id,
|
||||
'item_type' => Accessory::class,
|
||||
'created_by' => $admin->id,
|
||||
])->count(),
|
||||
'Log entry either does not exist or there are more than expected'
|
||||
);
|
||||
$this->assertHasTheseActionLogs($accessory, ['create', 'checkout']);
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'action_type' => 'checkout',
|
||||
'target_id' => $user->id,
|
||||
'target_type' => User::class,
|
||||
'item_id' => $accessory->id,
|
||||
'item_type' => Accessory::class,
|
||||
'quantity' => 2,
|
||||
'created_by' => $admin->id,
|
||||
]);
|
||||
|
||||
$this->assertHasTheseActionLogs($accessory, ['create', 'checkout']);
|
||||
}
|
||||
|
||||
public function testAccessoryCannotBeCheckedOutToInvalidUser()
|
||||
|
||||
@ -109,7 +109,7 @@ class ComponentCheckoutTest extends TestCase implements TestsFullMultipleCompani
|
||||
$this->actingAsForApi($user)
|
||||
->postJson(route('api.components.checkout', $component->id), [
|
||||
'assigned_to' => $asset->id,
|
||||
'assigned_qty' => 1,
|
||||
'assigned_qty' => 2,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
@ -120,6 +120,7 @@ class ComponentCheckoutTest extends TestCase implements TestsFullMultipleCompani
|
||||
'location_id' => $location->id,
|
||||
'item_type' => Component::class,
|
||||
'item_id' => $component->id,
|
||||
'quantity' => 2,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@ -52,6 +52,27 @@ class ConsumableCheckoutTest extends TestCase
|
||||
$this->assertHasTheseActionLogs($consumable, ['create', 'checkout']);
|
||||
}
|
||||
|
||||
public function testConsumableCanBeCheckedOutWithQuantity()
|
||||
{
|
||||
$consumable = Consumable::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->checkoutConsumables()->create())
|
||||
->postJson(route('api.consumables.checkout', $consumable), [
|
||||
'assigned_to' => $user->id,
|
||||
'checkout_qty' => 2,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'item_type' => Consumable::class,
|
||||
'item_id' => $consumable->id,
|
||||
'target_type' => User::class,
|
||||
'target_id' => $user->id,
|
||||
'action_type' => 'checkout',
|
||||
'quantity' => 2,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testUserSentNotificationUponCheckout()
|
||||
{
|
||||
Mail::fake();
|
||||
|
||||
@ -81,6 +81,7 @@ class AccessoryCheckoutTest extends TestCase
|
||||
'target_type' => User::class,
|
||||
'item_id' => $accessory->id,
|
||||
'item_type' => Accessory::class,
|
||||
'quantity' => 1,
|
||||
'note' => 'oh hi there',
|
||||
]);
|
||||
$this->assertHasTheseActionLogs($accessory, ['create', 'checkout']);
|
||||
@ -108,6 +109,7 @@ class AccessoryCheckoutTest extends TestCase
|
||||
'target_type' => User::class,
|
||||
'item_id' => $accessory->id,
|
||||
'item_type' => Accessory::class,
|
||||
'quantity' => 3,
|
||||
'note' => 'oh hi there',
|
||||
]);
|
||||
$this->assertHasTheseActionLogs($accessory, ['create', 'checkout']);
|
||||
@ -135,6 +137,7 @@ class AccessoryCheckoutTest extends TestCase
|
||||
'target_type' => Location::class,
|
||||
'item_id' => $accessory->id,
|
||||
'item_type' => Accessory::class,
|
||||
'quantity' => 3,
|
||||
'note' => 'oh hi there',
|
||||
]);
|
||||
$this->assertHasTheseActionLogs($accessory, ['create', 'checkout']);
|
||||
@ -162,6 +165,7 @@ class AccessoryCheckoutTest extends TestCase
|
||||
'target_type' => Asset::class,
|
||||
'item_id' => $accessory->id,
|
||||
'item_type' => Accessory::class,
|
||||
'quantity' => 3,
|
||||
'note' => 'oh hi there',
|
||||
]);
|
||||
$this->assertHasTheseActionLogs($accessory, ['create', 'checkout']);
|
||||
|
||||
@ -97,4 +97,30 @@ class ComponentsCheckoutTest extends TestCase
|
||||
->assertRedirect(route('hardware.show', $asset));
|
||||
$this->assertHasTheseActionLogs($component, ['create', 'checkout']);
|
||||
}
|
||||
|
||||
public function test_quantity_stored_in_action_log()
|
||||
{
|
||||
$component = Component::factory()->create();
|
||||
$asset = Asset::factory()->create();
|
||||
|
||||
$admin = User::factory()->admin()->create();
|
||||
|
||||
$this->actingAs($admin)
|
||||
->from(route('components.index'))
|
||||
->post(route('components.checkout.store', $component), [
|
||||
'asset_id' => $asset->id,
|
||||
'redirect_option' => 'index',
|
||||
'assigned_qty' => 2,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'action_type' => 'checkout',
|
||||
'target_id' => $asset->id,
|
||||
'target_type' => Asset::class,
|
||||
'item_id' => $component->id,
|
||||
'item_type' => Component::class,
|
||||
'quantity' => 2,
|
||||
'created_by' => $admin->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,4 +150,29 @@ class ConsumableCheckoutTest extends TestCase
|
||||
->assertRedirect(route('users.show', $user));
|
||||
}
|
||||
|
||||
public function test_quantity_stored_in_action_log()
|
||||
{
|
||||
$consumable = Consumable::factory()->create(['qty' => 3]);
|
||||
$user = User::factory()->create();
|
||||
|
||||
$admin = User::factory()->admin()->create();
|
||||
|
||||
$this->actingAs($admin)
|
||||
->from(route('components.index'))
|
||||
->post(route('consumables.checkout.store', $consumable), [
|
||||
'assigned_to' => $user->id,
|
||||
'redirect_option' => 'target',
|
||||
'checkout_qty' => 2,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'action_type' => 'checkout',
|
||||
'target_id' => $user->id,
|
||||
'target_type' => User::class,
|
||||
'item_id' => $consumable->id,
|
||||
'item_type' => Consumable::class,
|
||||
'quantity' => 2,
|
||||
'created_by' => $admin->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
53
tests/Unit/Models/LicenseTest.php
Normal file
53
tests/Unit/Models/LicenseTest.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Models;
|
||||
|
||||
use App\Enums\ActionType;
|
||||
use App\Models\License;
|
||||
use App\Models\User;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LicenseTest extends TestCase
|
||||
{
|
||||
public function test_adding_seats_is_logged_when_updating()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$this->actingAs($user);
|
||||
|
||||
$license = License::factory()->create(['seats' => 2]);
|
||||
|
||||
$license->update(['seats' => 6]);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'created_by' => $user->id,
|
||||
'action_type' => ActionType::AddSeats,
|
||||
'item_type' => License::class,
|
||||
'item_id' => $license->id,
|
||||
'deleted_at' => null,
|
||||
// relevant for this test:
|
||||
'quantity' => 4,
|
||||
'note' => 'added 4 seats',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_removing_seats_is_logged_when_updating()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$this->actingAs($user);
|
||||
|
||||
$license = License::factory()->create(['seats' => 6]);
|
||||
|
||||
$license->update(['seats' => 3]);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'created_by' => $user->id,
|
||||
'action_type' => ActionType::DeleteSeats,
|
||||
'item_type' => License::class,
|
||||
'item_id' => $license->id,
|
||||
'deleted_at' => null,
|
||||
// relevant for this test:
|
||||
'quantity' => 3,
|
||||
'note' => 'deleted 3 seats',
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user