Merge pull request #17703 from marcusmoore/17369-accessory-checkout-qty-scaffold

Fixed issues around accessory acceptance
This commit is contained in:
snipe
2025-09-08 09:09:26 +01:00
committed by GitHub
13 changed files with 131 additions and 21 deletions
@@ -233,6 +233,7 @@ class AcceptanceController extends Controller
'logo' => $path_logo,
'date_settings' => $branding_settings->date_display_format,
'admin' => auth()->user()->present()?->fullName,
'qty' => $acceptance->qty ?? 1,
];
if ($pdf_view_route!='') {
@@ -338,6 +339,7 @@ class AcceptanceController extends Controller
'assigned_to' => $assigned_to,
'company_name' => $branding_settings->site_name,
'date_settings' => $branding_settings->date_display_format,
'qty' => $acceptance->qty ?? 1,
];
if ($pdf_view_route!='') {
@@ -346,7 +348,10 @@ class AcceptanceController extends Controller
Storage::put('private_uploads/eula-pdfs/' .$pdf_filename, $pdf->output());
}
$acceptance->decline($sig_filename, $request->input('note'));
for ($i = 0; $i < ($acceptance->qty ?? 1); $i++) {
$acceptance->decline($sig_filename, $request->input('note'));
}
$acceptance->notify(new AcceptanceAssetDeclinedNotification($data));
Log::debug('New event acceptance.');
event(new CheckoutDeclined($acceptance));
+6
View File
@@ -242,6 +242,12 @@ class CheckoutableListener
$acceptance->checkoutable()->associate($event->checkoutable);
$acceptance->assignedTo()->associate($event->checkedOutTo);
$acceptance->qty = 1;
if (isset($event->checkoutable->checkout_qty)) {
$acceptance->qty = $event->checkoutable->checkout_qty;
}
$category = $this->getCategoryFromCheckoutable($event->checkoutable);
if ($category?->alert_on_response) {
+5
View File
@@ -148,4 +148,9 @@ class CheckoutAcceptance extends Model
{
return $query->whereNull('accepted_at')->whereNull('declined_at');
}
public function scopeDeclined(Builder $query)
{
return $query->whereNull('accepted_at')->whereNotNull('declined_at');
}
}
@@ -31,6 +31,7 @@ class AcceptanceAssetAcceptedNotification extends Notification
$this->company_name = $params['company_name'];
$this->admin = $params['admin'] ?? null;
$this->settings = Setting::getSettings();
$this->qty = $params['qty'] ?? null;
}
@@ -72,6 +73,7 @@ class AcceptanceAssetAcceptedNotification extends Notification
'accepted_date' => $this->accepted_date,
'assigned_to' => $this->assigned_to,
'company_name' => $this->company_name,
'qty' => $this->qty,
'intro_text' => trans('mail.acceptance_asset_accepted'),
'admin' => $this->admin,
])
@@ -29,7 +29,7 @@ class AcceptanceAssetAcceptedToUserNotification extends Notification
$this->company_name = $params['company_name'];
$this->settings = Setting::getSettings();
$this->file = $params['file'] ?? null;
$this->qty = $params['qty'] ?? null;
}
/**
@@ -66,6 +66,7 @@ class AcceptanceAssetAcceptedToUserNotification extends Notification
'accepted_date' => $this->accepted_date,
'assigned_to' => $this->assigned_to,
'company_name' => $this->company_name,
'qty' => $this->qty,
'intro_text' => trans('mail.acceptance_asset_accepted_to_user', ['site_name' => $this->company_name ?? $this->settings->site_name]),
])
->attach($pdf_path)
@@ -30,6 +30,7 @@ class AcceptanceAssetDeclinedNotification extends Notification
$this->assigned_to = $params['assigned_to'];
$this->company_name = $params['company_name'];
$this->settings = Setting::getSettings();
$this->qty = $params['qty'] ?? null;
}
/**
@@ -69,6 +70,7 @@ class AcceptanceAssetDeclinedNotification extends Notification
'declined_date' => $this->declined_date,
'assigned_to' => $this->assigned_to,
'company_name' => $this->company_name,
'qty' => $this->qty,
'intro_text' => trans('mail.acceptance_asset_declined'),
])
->subject(trans('mail.acceptance_asset_declined'));
@@ -0,0 +1,31 @@
<?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::whenTableDoesntHaveColumn('checkout_acceptances', 'qty', function () {
Schema::table('checkout_acceptances', function (Blueprint $table) {
$table->unsignedInteger('qty')->nullable()->after('assigned_to_id')->default(null);
});
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::whenTableHasColumn('checkout_acceptances', 'qty', function () {
Schema::table('checkout_acceptances', function (Blueprint $table) {
$table->dropColumn('qty');
});
});
}
};
+4 -2
View File
@@ -353,9 +353,11 @@ return [
'audit_overdue' => 'Overdue for Audit',
'accept' => 'Accept :asset',
'i_accept' => 'I accept',
'i_decline_item' => 'Decline this item',
'i_accept_item' => 'Accept this item',
'i_accept_with_count' => 'I accept :count item|I accept :count items',
'i_decline_item' => 'Decline this item|Decline these items',
'i_accept_item' => 'Accept this item|Accept these items',
'i_decline' => 'I decline',
'i_decline_with_count' => 'I decline :count item|I decline :count items',
'accept_decline' => 'Accept/Decline',
'sign_tos' => 'Sign below to indicate that you agree to the terms of service:',
'clear_signature' => 'Clear Signature',
@@ -24,6 +24,7 @@
<p>
{{ trans('general.date') }}: {{ date($date_settings) }} <br>
{{ trans('general.asset_model') }}: {{ $item_model }}<br>
{{ trans('general.quantity') }}: {{ $qty }}
</p>
@if ($eula)
@@ -44,4 +45,4 @@
<img src="{{ $signature }}" style="max-width: 600px; border-bottom: black solid 1px;">
@endif
</body>
</html>
</html>
+20 -14
View File
@@ -38,8 +38,13 @@
<div class="panel box box-default">
<div class="box-header with-border">
<h2 class="box-title">
{{ $acceptance->checkoutable->display_name }}
{{ (($acceptance->checkoutable) && ($acceptance->checkoutable->serial)) ? ' - '.trans('general.serial_number').': '.$acceptance->checkoutable->serial : '' }}
<div>
{{ $acceptance->checkoutable->display_name }}
@if ($acceptance->qty > 1)
<strong>×{{ $acceptance->qty }}</strong>
@endif
</div>
<div>{{ (($acceptance->checkoutable) && ($acceptance->checkoutable->serial)) ? trans('general.serial_number').': '.$acceptance->checkoutable->serial : '' }}</div>
</h2>
</div>
<div class="box-body">
@@ -53,11 +58,19 @@
<div class="col-md-12">
<label class="form-control">
<input type="radio" name="asset_acceptance" id="accepted" value="accepted">
{{trans('general.i_accept')}}
@if ($acceptance->qty)
{{trans_choice('general.i_accept_with_count', $acceptance->qty)}}
@else
{{trans('general.i_accept')}}
@endif
</label>
<label class="form-control">
<input type="radio" name="asset_acceptance" id="declined" value="declined">
{{trans('general.i_decline')}}
@if ($acceptance->qty)
{{trans_choice('general.i_decline_with_count', $acceptance->qty)}}
@else
{{trans('general.i_decline')}}
@endif
</label>
</div>
@@ -98,7 +111,7 @@
<button type="submit" class="btn btn-success" id="submit-button">
<i class="fa fa-check icon-white" aria-hidden="true" id="submitIcon"></i>
<span id="buttonText">
{{ trans('general.i_accept_item') }}
{{ trans_choice('general.i_accept_item', $acceptance->qty ?? null) }}
</span>
</button>
</div><!-- /.box-footer -->
@@ -161,7 +174,7 @@
$("#showSubmit").show();
$("#submit-button").removeClass("btn-success").addClass("btn-danger").show();
$("#submitIcon").removeClass("fa-check").addClass("fa-times");
$("#buttonText").text('{{ trans('general.i_decline_item') }}');
$("#buttonText").text('{{ trans_choice('general.i_decline_item', $acceptance->qty ?? 1) }}');
$("#note").prop('required', true);
} else if ($(this).is(':checked') && $(this).attr('id') === 'accepted') {
@@ -169,16 +182,9 @@
$("#showSubmit").show();
$("#submit-button").removeClass("btn-danger").addClass("btn-success").show();
$("#submitIcon").removeClass("fa-check").addClass("fa-check");
$("#buttonText").text('{{ trans('general.i_accept_item') }}');
$("#buttonText").text('{{ trans_choice('general.i_accept_item', $acceptance->qty ?? 1) }}');
$("#note").prop('required', false);
}
});
</script>
@stop
@@ -32,6 +32,7 @@
<tr>
<th>{{ trans('general.name')}}</th>
<th>{{ trans('general.type')}}</th>
<th>{{ trans('general.qty') }}</th>
<th>{{ trans('general.serial_number')}}</th>
<th>{{ trans('table.actions')}}</th>
</tr>
@@ -41,8 +42,9 @@
<tr>
@if ($acceptance->checkoutable)
<td>{{ ($acceptance->checkoutable) ? $acceptance->checkoutable->present()->name : '' }}</td>
<td>{{ $acceptance->checkoutable_item_type }}</td>
<td>{{ ($acceptance->checkoutable) ? $acceptance->checkoutable->serial : '' }}</td>
<td>{{ $acceptance->checkoutable_item_type }}</td>
<td>{{ $acceptance->qty ?? '1' }}</td>
<td>{{ ($acceptance->checkoutable) ? $acceptance->checkoutable->serial : '' }}</td>
<td><a href="{{ route('account.accept.item', $acceptance) }}" class="btn btn-default btn-sm">{{ trans('general.accept_decline') }}</a></td>
@else
<td> ----- </td>
@@ -37,6 +37,9 @@
@if (isset($item_serial))
| **{{ trans('mail.serial') }}** | {{ $item_serial }} |
@endif
@if (isset($qty))
| **{{ trans('general.qty') }}** | {{ $qty }} |
@endif
@if (isset($admin))
| **{{ trans('general.administrator') }}** | {{ $admin }} |
@endif
@@ -3,6 +3,7 @@
namespace Tests\Feature\CheckoutAcceptances\Ui;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\User;
@@ -96,4 +97,47 @@ class AccessoryAcceptanceTest extends TestCase
$this->assertNull($acceptance->fresh()->accepted_at);
}
/**
* @link https://github.com/grokability/snipe-it/issues/17589
*/
public function test_all_accessory_checkout_entries_are_removed_when_user_declines_acceptance()
{
$assignee = User::factory()->create();
$this->actingAs(User::factory()->checkoutAccessories()->create());
// create accessory that requires acceptance
$accessory = Accessory::factory()->requiringAcceptance()->create(['qty' => 5]);
// checkout 3 accessories to the user
$this->post(route('accessories.checkout.store', $accessory), [
'assigned_user' => $assignee->id,
'checkout_qty' => 3,
]);
$originalAccessoryCheckoutCount = AccessoryCheckout::count();
// find the acceptance to be declined
$checkoutAcceptance = CheckoutAcceptance::query()
->where([
'assigned_to_id' => $assignee->id,
'qty' => 3,
])
->whereNull('accepted_at')
->whereNull('declined_at')
->whereHasMorph(
'checkoutable',
[Accessory::class],
)
->sole();
// decline the checkout
$this->actingAs($assignee)
->post(route('account.store-acceptance', $checkoutAcceptance), [
'asset_acceptance' => 'declined',
]);
$this->assertEquals($originalAccessoryCheckoutCount - 3, AccessoryCheckout::count());
}
}