Merge remote-tracking branch 'origin/develop'

This commit is contained in:
snipe 2024-03-26 08:23:40 +00:00
commit bd42505799
7 changed files with 200 additions and 34 deletions

View file

@ -613,7 +613,7 @@ class AssetsController extends Controller
$asset->image = $asset->getImageUrl();
}
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.create.success')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
@ -692,7 +692,7 @@ class AssetsController extends Controller
$asset->image = $asset->getImageUrl();
}
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);

View file

@ -4,6 +4,8 @@ namespace App\Http\Requests;
use App\Models\Asset;
use App\Models\Company;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Support\Facades\Gate;
class StoreAssetRequest extends ImageUploadRequest
@ -27,6 +29,8 @@ class StoreAssetRequest extends ImageUploadRequest
? Company::getIdForCurrentUser($this->company_id)
: $this->company_id;
$this->parseLastAuditDate();
$this->merge([
'asset_tag' => $this->asset_tag ?? Asset::autoincrement_asset(),
'company_id' => $idForCurrentUser,
@ -48,4 +52,21 @@ class StoreAssetRequest extends ImageUploadRequest
return $rules;
}
private function parseLastAuditDate(): void
{
if ($this->input('last_audit_date')) {
try {
$lastAuditDate = Carbon::parse($this->input('last_audit_date'));
$this->merge([
'last_audit_date' => $lastAuditDate->startOfDay()->format('Y-m-d H:i:s'),
]);
} catch (InvalidFormatException $e) {
// we don't need to do anything here...
// we'll keep the provided date in an
// invalid format so validation picks it up later
}
}
}
}

View file

@ -97,6 +97,8 @@ class Asset extends Depreciable
'warranty_months' => 'nullable|numeric|digits_between:0,240',
'last_checkout' => 'nullable|date_format:Y-m-d H:i:s',
'expected_checkin' => 'nullable|date',
'last_audit_date' => 'nullable|date_format:Y-m-d H:i:s',
'next_audit_date' => 'nullable|date|after:last_audit_date',
'location_id' => 'nullable|exists:locations,id',
'rtd_location_id' => 'nullable|exists:locations,id',
'purchase_date' => 'nullable|date|date_format:Y-m-d',

View file

@ -0,0 +1,89 @@
<?php
namespace App\Models\Labels\Tapes\Dymo;
class LabelWriter_2112283 extends LabelWriter
{
private const BARCODE_MARGIN = 1.80;
private const TAG_SIZE = 2.80;
private const TITLE_SIZE = 2.80;
private const TITLE_MARGIN = 0.50;
private const LABEL_SIZE = 2.80;
private const LABEL_MARGIN = - 0.35;
private const FIELD_SIZE = 2.80;
private const FIELD_MARGIN = 0.15;
public function getUnit() { return 'mm'; }
public function getWidth() { return 54; }
public function getHeight() { return 25; }
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return true; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 5; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return true; }
public function preparePDF($pdf) {}
public function write($pdf, $record) {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
$currentY = $pa->y1;
$usableWidth = $pa->w;
$barcodeSize = $pa->h - self::TAG_SIZE;
if ($record->has('barcode2d')) {
static::writeText(
$pdf, $record->get('tag'),
$pa->x1, $pa->y2 - self::TAG_SIZE,
'freesans', 'b', self::TAG_SIZE, 'C',
$barcodeSize, self::TAG_SIZE, true, 0
);
static::write2DBarcode(
$pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
$currentX, $currentY,
$barcodeSize, $barcodeSize
);
$currentX += $barcodeSize + self::BARCODE_MARGIN;
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
} else {
static::writeText(
$pdf, $record->get('tag'),
$pa->x1, $pa->y2 - self::TAG_SIZE,
'freesans', 'b', self::TAG_SIZE, 'R',
$usableWidth, self::TAG_SIZE, true, 0
);
}
if ($record->has('title')) {
static::writeText(
$pdf, $record->get('title'),
$currentX, $currentY,
'freesans', 'b', self::TITLE_SIZE, 'L',
$usableWidth, self::TITLE_SIZE, true, 0
);
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
foreach ($record->get('fields') as $field) {
static::writeText(
$pdf, (($field['label']) ? $field['label'].' ' : '') . $field['value'],
$currentX, $currentY,
'freesans', '', self::FIELD_SIZE, 'L',
$usableWidth, self::FIELD_SIZE, true, 0, 0.3
);
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
}
if ($record->has('barcode1d')) {
static::write1DBarcode(
$pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type,
$currentX, $barcodeSize + self::BARCODE_MARGIN, $usableWidth - self::TAG_SIZE, self::TAG_SIZE
);
}
}
}

View file

@ -904,27 +904,18 @@
@endcan
@can('delete', $asset)
@if ($asset->deleted_at=='')
<div class="col-md-12" style="padding-top: 30px; padding-bottom: 30px;">
<button class="btn btn-block btn-danger delete-asset" data-toggle="modal" data-title="{{ trans('general.delete') }}" data-content="{{ trans('general.sure_to_delete', ['item' => $asset->asset_tag]) }}" data-target="#dataConfirmModal">{{ trans('general.delete') }} </button>
<div class="col-md-12" style="padding-top: 30px; padding-bottom: 30px;">
@if ($asset->deleted_at=='')
<button class="btn btn-sm btn-block btn-danger delete-asset" data-toggle="modal" data-title="{{ trans('general.delete') }}" data-content="{{ trans('general.sure_to_delete_var', ['item' => $asset->asset_tag]) }}" data-target="#dataConfirmModal">{{ trans('general.delete') }} </button>
<span class="sr-only">{{ trans('general.delete') }}</span>
</div>
@endif
@else
<form method="POST" action="{{ route('restore/hardware', ['assetId' => $asset->id]) }}">
@csrf
<button class="btn btn-sm btn-warning col-md-12">{{ trans('general.restore') }}</button>
</form>
@endif
@endcan
@if ($asset->deleted_at!='')
<div class="text-center col-md-12" style="padding-top: 30px; padding-bottom: 30px;">
<form method="POST" action="{{ route('restore/hardware', ['assetId' => $asset->id]) }}">
@csrf
<button class="btn btn-danger col-md-12">{{ trans('general.restore') }}</button>
</form>
</div>
@endif
@if ($snipeSettings->qr_code=='1')
<img src="{{ config('app.url') }}/hardware/{{ $asset->id }}/qr_code" class="img-thumbnail pull-right" style="height: 100px; width: 100px; margin-right: 10px;" alt="QR code for {{ $asset->getDisplayNameAttribute() }}">
@endif
@if (($asset->assignedTo) && ($asset->deleted_at==''))
<div style="text-align: left">
<h2>{{ trans('admin/hardware/form.checkedout_to') }}</h2>
@ -982,6 +973,13 @@
</div>
@endif
@if ($snipeSettings->qr_code=='1')
<div class="col-md-12" style="padding-top: 15px;">
<img src="{{ config('app.url') }}/hardware/{{ $asset->id }}/qr_code" class="img-thumbnail pull-right" style="height: 100px; width: 100px; margin-right: 10px;" alt="QR code for {{ $asset->getDisplayNameAttribute() }}">
</div>
@endif
</div> <!-- div.col-md-4 -->
</div><!-- /row -->
</div><!-- /.tab-pane asset details -->

View file

@ -236,6 +236,12 @@
</li>
@endif
@if ($model->created_at)
<li>{{ trans('general.created_at') }}:
{{ Helper::getFormattedDateObject($model->created_at, 'datetime', false) }}
</li>
@endif
@if ($model->min_amt)
<li>{{ trans('general.min_amt') }}:
{{$model->min_amt }}
@ -313,11 +319,6 @@
</li>
@endif
@if ($model->deleted_at!='')
<li><br /><a href="{{ route('models.restore.store', $model->id) }}" class="btn-flat large info ">{{ trans('admin/models/general.restore') }}</a></li>
@endif
</ul>
@if ($model->note)
@ -337,22 +338,32 @@
@can('create', \App\Models\AssetModel::class)
<div class="col-md-12" style="padding-bottom: 5px;">
<a href="{{ route('models.clone.create', $model->id) }}" style="width: 100%;" class="btn btn-sm btn-warning hidden-print">{{ trans('admin/models/table.clone') }}</a>
<a href="{{ route('models.clone.create', $model->id) }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print">{{ trans('admin/models/table.clone') }}</a>
</div>
@endcan
@can('delete', \App\Models\AssetModel::class)
@if ($model->assets_count > 0)
<div class="col-md-12" style="padding-bottom: 5px;">
<button class="btn btn-block btn-sm btn-danger hidden-print disabled" data-tooltip="true" data-placement="top" data-title="{{ trans('general.cannot_be_deleted') }}">{{ trans('general.delete') }}</button>
<button class="btn btn-block btn-sm btn-primary hidden-print disabled" data-tooltip="true" data-placement="top" data-title="{{ trans('general.cannot_be_deleted') }}">{{ trans('general.delete') }}</button>
</div>
@else
<div class="col-md-12" style="padding-bottom: 10px;">
<button class="btn btn-block btn-danger delete-asset" data-toggle="modal" title="{{ trans('general.delete_what', ['item'=> trans('general.asset_model')]) }}" data-content="{{ trans('general.sure_to_delete_var', ['item' => $model->name]) }}" data-target="#dataConfirmModal" data-tooltip="true" data-placement="top" data-title="{{ trans('general.delete_what', ['item'=> trans('general.asset_model')]) }}">{{ trans('general.delete') }} </button>
<span class="sr-only">{{ trans('general.delete') }}</span>
</div>
@endif
<div class="text-center col-md-12" style="padding-top: 30px; padding-bottom: 30px;">
@if ($model->deleted_at!='')
<form method="POST" action="{{ route('models.restore.store', $model->id) }}">
@csrf
<button style="width: 100%;" class="btn btn-sm btn-warning hidden-print">{{ trans('button.restore') }}</button>
</form>
@else
<button class="btn btn-block btn-sm btn-danger delete-asset" data-toggle="modal" title="{{ trans('general.delete_what', ['item'=> trans('general.asset_model')]) }}" data-content="{{ trans('general.sure_to_delete_var', ['item' => $model->name]) }}" data-target="#dataConfirmModal" data-tooltip="true" data-placement="top" data-title="{{ trans('general.delete_what', ['item'=> trans('general.asset_model')]) }}">{{ trans('general.delete') }} </button>
<span class="sr-only">{{ trans('general.delete') }}</span>
@endif
</div>
@endcan
</div>
</div> <!-- /.row -->

View file

@ -65,8 +65,7 @@ class AssetStoreTest extends TestCase
$this->assertEquals('random_string', $asset->asset_tag);
$this->assertEquals($userAssigned->id, $asset->assigned_to);
$this->assertTrue($asset->company->is($company));
// I don't see this on the GUI side either, but it's in the docs so I'm guessing that's a mistake? It wasn't in the controller.
// $this->assertEquals('2023-09-03', $asset->last_audit_date);
$this->assertEquals('2023-09-03 00:00:00', $asset->last_audit_date->format('Y-m-d H:i:s'));
$this->assertTrue($asset->location->is($location));
$this->assertTrue($asset->model->is($model));
$this->assertEquals('A New Asset', $asset->name);
@ -82,6 +81,52 @@ class AssetStoreTest extends TestCase
$this->assertEquals(10, $asset->warranty_months);
}
public function testSetsLastAuditDateToMidnightOfProvidedDate()
{
$response = $this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'last_audit_date' => '2023-09-03 12:23:45',
'asset_tag' => '1234',
'model_id' => AssetModel::factory()->create()->id,
'status_id' => Statuslabel::factory()->create()->id,
])
->assertOk()
->assertStatusMessageIs('success');
$asset = Asset::find($response['payload']['id']);
$this->assertEquals('00:00:00', $asset->last_audit_date->format('H:i:s'));
}
public function testLastAuditDateCanBeNull()
{
$response = $this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
// 'last_audit_date' => '2023-09-03 12:23:45',
'asset_tag' => '1234',
'model_id' => AssetModel::factory()->create()->id,
'status_id' => Statuslabel::factory()->create()->id,
])
->assertOk()
->assertStatusMessageIs('success');
$asset = Asset::find($response['payload']['id']);
$this->assertNull($asset->last_audit_date);
}
public function testNonDateUsedForLastAuditDateReturnsValidationError()
{
$response = $this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'last_audit_date' => 'this-is-not-valid',
'asset_tag' => '1234',
'model_id' => AssetModel::factory()->create()->id,
'status_id' => Statuslabel::factory()->create()->id,
])
->assertStatusMessageIs('error');
$this->assertNotNull($response->json('messages.last_audit_date'));
}
public function testArchivedDepreciateAndPhysicalCanBeNull()
{
$model = AssetModel::factory()->ipadModel()->create();