Skip to content
Original file line number Diff line number Diff line change
@@ -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;
}
}
4 changes: 3 additions & 1 deletion app/Events/CheckoutableCheckedOut.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function create($id) : View | RedirectResponse
*/
public function store(AccessoryCheckoutRequest $request, Accessory $accessory) : RedirectResponse
{

$this->authorize('checkout', $accessory);

$target = $this->determineCheckoutTarget();
Expand All @@ -89,7 +89,14 @@ public function store(AccessoryCheckoutRequest $request, Accessory $accessory) :
$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]);
Expand Down
9 changes: 8 additions & 1 deletion app/Http/Controllers/Api/AccessoriesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,14 @@ public function checkout(AccessoryCheckoutRequest $request, Accessory $accessory
}

// 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')));

Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/Api/ComponentsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ public function checkout(Request $request, $componentId) : JsonResponse
'note' => $request->get('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')));
}
Expand Down
10 changes: 8 additions & 2 deletions app/Http/Controllers/Api/ConsumablesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,14 @@ public function checkout(Request $request, $id) : JsonResponse
);
}


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')));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,14 @@ public function store(Request $request, $componentId)
'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]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,15 @@ public function store(Request $request, $consumableId)
}

$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]);
Expand Down
25 changes: 23 additions & 2 deletions app/Http/Transformers/ActionlogsTransformer.php
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -80,7 +81,7 @@ public function transformActionlog (Actionlog $actionlog, $settings = null)

// this is a custom field
if (str_starts_with($fieldname, '_snipeit_')) {

foreach ($custom_fields as $custom_field) {

if ($custom_field->db_column == $fieldname) {
Expand Down Expand Up @@ -185,7 +186,7 @@ public function transformActionlog (Actionlog $actionlog, $settings = null)
'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,
Expand Down Expand Up @@ -336,6 +337,26 @@ public function changedInfo(array $clean_meta)

}

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;
}


}
10 changes: 9 additions & 1 deletion app/Listeners/LogListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ public function onCheckoutableCheckedIn(CheckoutableCheckedIn $event)
*/
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
);
}

/**
Expand All @@ -66,6 +72,7 @@ public function onCheckoutAccepted(CheckoutAccepted $event)
$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) {
Expand All @@ -84,6 +91,7 @@ public function onCheckoutDeclined(CheckoutDeclined $event)
$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) {
Expand Down
2 changes: 2 additions & 0 deletions app/Models/License.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ public static function adjustSeatCount($license, $oldSeats, $newSeats)
$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;
Expand Down Expand Up @@ -259,6 +260,7 @@ function () use ($chunk) {
$logAction->created_by = auth()->id() ?: 1; // Importer.
$logAction->note = "added {$change} seats";
$logAction->target_id = null;
$logAction->quantity = $change;
$logAction->logaction('add seats');
}

Expand Down
4 changes: 2 additions & 2 deletions app/Models/Traits/Loggable.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function setImported(bool $bool): void
* @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;
Expand Down Expand Up @@ -94,7 +94,7 @@ public function logCheckout($note, $target, $action_date = null, $originalValues

$log->note = $note;
$log->action_date = $action_date;

$log->quantity = $quantity;

$changed = [];
$array_to_flip = array_keys($fields_array);
Expand Down
9 changes: 8 additions & 1 deletion app/Presenters/HistoryPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ public static function dataTableLayout($serial = false)
'visible' => true,
'formatter' => 'fileDownloadButtonsFormatter',
],
[
'field' => 'quantity',
'searchable' => false,
'sortable' => true,
'visible' => true,
'title' => trans('general.quantity'),
],
[
'field' => 'note',
'searchable' => true,
Expand Down Expand Up @@ -169,4 +176,4 @@ public static function dataTableLayout($serial = false)
return json_encode($merged);
}

}
}
Original file line number Diff line number Diff line change
@@ -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');
});
}
};
Loading
Loading