mirror of
https://github.com/snipe/snipe-it.git
synced 2025-03-05 20:52:15 -08:00
Merge pull request #14800 from snipe/fixes/importer_tweaks
Importer tweaks
This commit is contained in:
commit
cacb5d62dc
|
@ -8,7 +8,6 @@ use Livewire\Component;
|
|||
use App\Models\Import;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
|
||||
|
||||
|
@ -119,8 +118,7 @@ class Importer extends Component
|
|||
public function updating($name, $new_import_type)
|
||||
{
|
||||
if ($name == "activeFile.import_type") {
|
||||
Log::debug("WE ARE CHANGING THE import_type!!!!! TO: " . $new_import_type);
|
||||
Log::debug("so, what's \$this->>field_map at?: " . print_r($this->field_map, true));
|
||||
|
||||
// go through each header, find a matching field to try and map it to.
|
||||
foreach ($this->activeFile->header_row as $i => $header) {
|
||||
// do we have something mapped already?
|
||||
|
@ -237,6 +235,15 @@ class Importer extends Component
|
|||
'email' => trans('general.importer.checked_out_to_email'),
|
||||
'username' => trans('general.importer.checked_out_to_username'),
|
||||
'checkout_location' => trans('general.importer.checkout_location'),
|
||||
/**
|
||||
* These are here so users can import history, to replace the dinosaur that
|
||||
* was the history importer
|
||||
*/
|
||||
'last_checkin' => trans('admin/hardware/table.last_checkin_date'),
|
||||
'last_checkout' => trans('admin/hardware/table.checkout_date'),
|
||||
'expected_checkin' => trans('admin/hardware/form.expected_checkin'),
|
||||
'last_audit_date' => trans('general.last_audit'),
|
||||
'next_audit_date' => trans('general.next_audit_date'),
|
||||
];
|
||||
|
||||
$this->consumables_fields = [
|
||||
|
@ -380,6 +387,12 @@ class Importer extends Component
|
|||
'job title for user',
|
||||
'job title',
|
||||
],
|
||||
'full_name' =>
|
||||
[
|
||||
'full name',
|
||||
'fullname',
|
||||
trans('general.importer.checked_out_to_fullname')
|
||||
],
|
||||
'username' =>
|
||||
[
|
||||
'user name',
|
||||
|
@ -412,6 +425,7 @@ class Importer extends Component
|
|||
'telephone',
|
||||
'tel.',
|
||||
],
|
||||
|
||||
'serial' =>
|
||||
[
|
||||
'serial number',
|
||||
|
@ -456,6 +470,12 @@ class Importer extends Component
|
|||
[
|
||||
'Next Audit',
|
||||
],
|
||||
'last_checkout' =>
|
||||
[
|
||||
'Last Checkout',
|
||||
'Last Checkout Date',
|
||||
'Checkout Date',
|
||||
],
|
||||
'address2' =>
|
||||
[
|
||||
'Address 2',
|
||||
|
@ -523,9 +543,8 @@ class Importer extends Component
|
|||
{
|
||||
// TODO: why don't we just do File::find($id)? This seems dumb.
|
||||
foreach($this->files as $file) {
|
||||
Log::debug("File id is: ".$file->id);
|
||||
if($id == $file->id) {
|
||||
if(Storage::delete('private_uploads/imports/'.$file->file_path)) {
|
||||
if ($id == $file->id) {
|
||||
if (Storage::delete('private_uploads/imports/'.$file->file_path)) {
|
||||
$file->delete();
|
||||
|
||||
$this->message = trans('admin/hardware/message.import.file_delete_success');
|
||||
|
|
|
@ -7,8 +7,10 @@ use App\Models\AssetModel;
|
|||
use App\Models\Statuslabel;
|
||||
use App\Models\User;
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AssetImporter extends ItemImporter
|
||||
{
|
||||
|
@ -19,7 +21,7 @@ class AssetImporter extends ItemImporter
|
|||
parent::__construct($filename);
|
||||
|
||||
if (!is_null(Statuslabel::first())) {
|
||||
$this->defaultStatusLabelId = Statuslabel::first()->id;
|
||||
$this->defaultStatusLabelId = Statuslabel::deployable()->first()->id;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,16 +66,14 @@ class AssetImporter extends ItemImporter
|
|||
$editingAsset = false;
|
||||
$asset_tag = $this->findCsvMatch($row, 'asset_tag');
|
||||
|
||||
if(empty($asset_tag)){
|
||||
if (empty($asset_tag)){
|
||||
$asset_tag = Asset::autoincrement_asset();
|
||||
}
|
||||
|
||||
|
||||
$asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first();
|
||||
if ($asset) {
|
||||
if (! $this->updating) {
|
||||
$this->log('A matching Asset '.$asset_tag.' already exists');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,13 @@ class AssetImporter extends ItemImporter
|
|||
$this->log('No Matching Asset, Creating a new one');
|
||||
$asset = new Asset;
|
||||
}
|
||||
|
||||
// If no status ID is found
|
||||
if (! array_key_exists('status_id', $this->item) && ! $editingAsset) {
|
||||
$this->log('No status ID field found, defaulting to first deployable status label.');
|
||||
$this->item['status_id'] = $this->defaultStatusLabelId;
|
||||
}
|
||||
|
||||
$this->item['notes'] = trim($this->findCsvMatch($row, 'asset_notes'));
|
||||
$this->item['image'] = trim($this->findCsvMatch($row, 'image'));
|
||||
$this->item['requestable'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable'))) == 1) ? '1' : 0;
|
||||
|
@ -90,14 +97,12 @@ class AssetImporter extends ItemImporter
|
|||
$this->item['warranty_months'] = intval(trim($this->findCsvMatch($row, 'warranty_months')));
|
||||
$this->item['model_id'] = $this->createOrFetchAssetModel($row);
|
||||
$this->item['byod'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'byod'))) == 1) ? '1' : 0;
|
||||
|
||||
|
||||
// If no status ID is found
|
||||
if (! array_key_exists('status_id', $this->item) && ! $editingAsset) {
|
||||
$this->log('No status field found, defaulting to first status.');
|
||||
$this->item['status_id'] = $this->defaultStatusLabelId;
|
||||
}
|
||||
|
||||
$this->item['last_checkin'] = trim($this->findCsvMatch($row, 'last_checkin'));
|
||||
$this->item['last_checkout'] = trim($this->findCsvMatch($row, 'last_checkout'));
|
||||
$this->item['expected_checkin'] = trim($this->findCsvMatch($row, 'expected_checkin'));
|
||||
$this->item['last_audit_date'] = trim($this->findCsvMatch($row, 'last_audit_date'));
|
||||
$this->item['next_audit_date'] = trim($this->findCsvMatch($row, 'next_audit_date'));
|
||||
$this->item['asset_eol_date'] = trim($this->findCsvMatch($row, 'next_audit_date'));
|
||||
$this->item['asset_tag'] = $asset_tag;
|
||||
|
||||
// We need to save the user if it exists so that we can checkout to user later.
|
||||
|
@ -105,7 +110,9 @@ class AssetImporter extends ItemImporter
|
|||
if (array_key_exists('checkout_target', $this->item)) {
|
||||
$target = $this->item['checkout_target'];
|
||||
}
|
||||
|
||||
$item = $this->sanitizeItemForStoring($asset, $editingAsset);
|
||||
|
||||
// The location id fetched by the csv reader is actually the rtd_location_id.
|
||||
// This will also set location_id, but then that will be overridden by the
|
||||
// checkout method if necessary below.
|
||||
|
@ -113,16 +120,42 @@ class AssetImporter extends ItemImporter
|
|||
$item['rtd_location_id'] = $this->item['location_id'];
|
||||
}
|
||||
|
||||
$item['last_audit_date'] = null;
|
||||
if (isset($this->item['last_audit_date'])) {
|
||||
$item['last_audit_date'] = $this->item['last_audit_date'];
|
||||
|
||||
/**
|
||||
* We use this to backdate the checkin action further down
|
||||
*/
|
||||
$checkin_date = date('Y-m-d H:i:s');
|
||||
if ($this->item['last_checkin']!='') {
|
||||
$item['last_checkin'] = $this->parseOrNullDate('last_checkin', 'datetime');
|
||||
$checkout_date = $this->item['last_checkin'];
|
||||
}
|
||||
|
||||
$item['next_audit_date'] = null;
|
||||
if (isset($this->item['next_audit_date'])) {
|
||||
$item['next_audit_date'] = $this->item['next_audit_date'];
|
||||
/**
|
||||
* We use this to backdate the checkout action further down
|
||||
*/
|
||||
$checkout_date = date('Y-m-d H:i:s');
|
||||
if ($this->item['last_checkout']!='') {
|
||||
$item['last_checkout'] = $this->parseOrNullDate('last_checkout', 'datetime');
|
||||
$checkout_date = $this->item['last_checkout'];
|
||||
}
|
||||
|
||||
|
||||
if ($this->item['expected_checkin']!='') {
|
||||
$item['expected_checkin'] = $this->parseOrNullDate('expected_checkin');
|
||||
}
|
||||
|
||||
if ($this->item['last_audit_date']!='') {
|
||||
$item['last_audit_date'] = $this->parseOrNullDate('last_audit_date');
|
||||
}
|
||||
|
||||
if ($this->item['next_audit_date']!='') {
|
||||
$item['next_audit_date'] = $this->parseOrNullDate('next_audit_date');
|
||||
}
|
||||
|
||||
if ($this->item['asset_eol_date']!='') {
|
||||
$item['asset_eol_date'] = $this->parseOrNullDate('asset_eol_date');
|
||||
}
|
||||
|
||||
|
||||
if ($editingAsset) {
|
||||
$asset->update($item);
|
||||
} else {
|
||||
|
@ -135,27 +168,31 @@ class AssetImporter extends ItemImporter
|
|||
$asset->{$custom_field} = $val;
|
||||
}
|
||||
}
|
||||
|
||||
// This sets an attribute on the Loggable trait for the action log
|
||||
$asset->setImported(true);
|
||||
|
||||
if ($asset->save()) {
|
||||
|
||||
$this->log('Asset '.$this->item['name'].' with serial number '.$this->item['serial'].' was created');
|
||||
|
||||
// If we have a target to checkout to, lets do so.
|
||||
//-- user_id is a property of the abstract class Importer, which this class inherits from and it's setted by
|
||||
//-- user_id is a property of the abstract class Importer, which this class inherits from and it's set by
|
||||
//-- the class that needs to use it (command importer or GUI importer inside the project).
|
||||
if (isset($target) && ($target !== false)) {
|
||||
if (!is_null($asset->assigned_to)){
|
||||
if ($asset->assigned_to != $target->id){
|
||||
event(new CheckoutableCheckedIn($asset, User::find($asset->assigned_to), Auth::user(), $asset->notes, date('Y-m-d H:i:s')));
|
||||
if ($asset->assigned_to != $target->id) {
|
||||
event(new CheckoutableCheckedIn($asset, User::find($asset->assigned_to), Auth::user(), 'Checkin from CSV Importer', $checkin_date));
|
||||
}
|
||||
}
|
||||
|
||||
$asset->fresh()->checkOut($target, $this->user_id, date('Y-m-d H:i:s'), null, $asset->notes, $asset->name);
|
||||
$asset->fresh()->checkOut($target, $this->user_id, $checkout_date, null, 'Checkout from CSV Importer', $asset->name);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
$this->logError($asset, 'Asset "'.$this->item['name'].'"');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use App\Models\CustomField;
|
|||
use App\Models\Department;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Carbon\CarbonImmutable;
|
||||
use ForceUTF8\Encoding;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
@ -551,4 +552,35 @@ abstract class Importer
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a date or return null
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since 7.0.0
|
||||
* @param $field
|
||||
* @param $format
|
||||
* @return string|null
|
||||
|
||||
*/
|
||||
public function parseOrNullDate($field, $format = 'date') {
|
||||
|
||||
$date_format = 'Y-m-d';
|
||||
|
||||
if ($format == 'datetime') {
|
||||
$date_format = 'Y-m-d H:i:s';
|
||||
}
|
||||
|
||||
if (array_key_exists($field, $this->item) && $this->item[$field] != '') {
|
||||
|
||||
try {
|
||||
$value = CarbonImmutable::parse($this->item[$field])->format($date_format);
|
||||
return $value;
|
||||
} catch (\Exception $e) {
|
||||
$this->log('Unable to parse date: ' . $this->item[$field]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,26 +79,18 @@ class ItemImporter extends Importer
|
|||
$this->item['purchase_date'] = date('Y-m-d', strtotime($this->findCsvMatch($row, 'purchase_date')));
|
||||
}
|
||||
|
||||
$this->item['last_audit_date'] = null;
|
||||
if ($this->findCsvMatch($row, 'last_audit_date') != '') {
|
||||
$this->item['last_audit_date'] = date('Y-m-d', strtotime($this->findCsvMatch($row, 'last_audit_date')));
|
||||
}
|
||||
// $this->item['asset_eol_date'] = null;
|
||||
// if ($this->findCsvMatch($row, 'asset_eol_date') != '') {
|
||||
// $csvMatch = $this->findCsvMatch($row, 'asset_eol_date');
|
||||
// \Log::warning('EOL Date for $csvMatch is '.$csvMatch);
|
||||
// try {
|
||||
// $this->item['asset_eol_date'] = CarbonImmutable::parse($csvMatch)->format('Y-m-d');
|
||||
// } catch (\Exception $e) {
|
||||
// Log::info($e->getMessage());
|
||||
// $this->log('Unable to parse date: '.$csvMatch);
|
||||
// }
|
||||
// }
|
||||
|
||||
$this->item['next_audit_date'] = null;
|
||||
if ($this->findCsvMatch($row, 'next_audit_date') != '') {
|
||||
$this->item['next_audit_date'] = date('Y-m-d', strtotime($this->findCsvMatch($row, 'next_audit_date')));
|
||||
}
|
||||
|
||||
$this->item['asset_eol_date'] = null;
|
||||
if($this->findCsvMatch($row, 'asset_eol_date') != '') {
|
||||
$csvMatch = $this->findCsvMatch($row, 'asset_eol_date');
|
||||
try {
|
||||
$this->item['asset_eol_date'] = CarbonImmutable::parse($csvMatch)->format('Y-m-d');
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e->getMessage());
|
||||
$this->log('Unable to parse date: '.$csvMatch);
|
||||
}
|
||||
}
|
||||
|
||||
$this->item['qty'] = $this->findCsvMatch($row, 'quantity');
|
||||
$this->item['requestable'] = $this->findCsvMatch($row, 'requestable');
|
||||
|
@ -389,7 +381,6 @@ class ItemImporter extends Importer
|
|||
|
||||
if ($status->save()) {
|
||||
$this->log('Status '.$asset_statuslabel_name.' was created');
|
||||
|
||||
return $status->id;
|
||||
}
|
||||
|
||||
|
@ -509,4 +500,5 @@ class ItemImporter extends Importer
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use App\Http\Traits\UniqueUndeletedTrait;
|
|||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use AssetPresenter;
|
||||
use App\Presenters\AssetPresenter;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
@ -19,6 +19,8 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* Model for Assets.
|
||||
|
@ -28,7 +30,7 @@ use Watson\Validating\ValidatingTrait;
|
|||
class Asset extends Depreciable
|
||||
{
|
||||
|
||||
protected $presenter = \App\Presenters\AssetPresenter::class;
|
||||
protected $presenter = AssetPresenter::class;
|
||||
|
||||
use CompanyableTrait;
|
||||
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
|
||||
|
@ -149,6 +151,8 @@ class Asset extends Depreciable
|
|||
'last_audit_date',
|
||||
'next_audit_date',
|
||||
'asset_eol_date',
|
||||
'last_checkin',
|
||||
'last_checkout',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
@ -971,14 +975,37 @@ class Asset extends Depreciable
|
|||
* @param $value
|
||||
* @return void
|
||||
*/
|
||||
public function getNextAuditDateAttribute($value)
|
||||
|
||||
protected function nextAuditDate(): Attribute
|
||||
{
|
||||
return $this->attributes['next_audit_date'] = $value ? Carbon::parse($value)->format('Y-m-d') : null;
|
||||
return Attribute::make(
|
||||
get: fn ($value) => $value ? Carbon::parse($value)->format('Y-m-d') : null,
|
||||
set: fn ($value) => $value ? Carbon::parse($value)->format('Y-m-d') : null,
|
||||
);
|
||||
}
|
||||
|
||||
public function setNextAuditDateAttribute($value)
|
||||
protected function lastCheckout(): Attribute
|
||||
{
|
||||
$this->attributes['next_audit_date'] = $value ? Carbon::parse($value)->format('Y-m-d') : null;
|
||||
return Attribute::make(
|
||||
get: fn ($value) => $value ? Carbon::parse($value)->format('Y-m-d H:i:s') : null,
|
||||
set: fn ($value) => $value ? Carbon::parse($value)->format('Y-m-d H:i:s') : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function lastCheckin(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn ($value) => $value ? Carbon::parse($value)->format('Y-m-d H:i:s') : null,
|
||||
set: fn ($value) => $value ? Carbon::parse($value)->format('Y-m-d H:i:s') : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function assetEolDate(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn ($value) => $value ? Carbon::parse($value)->format('Y-m-d') : null,
|
||||
set: fn ($value) => $value ? Carbon::parse($value)->format('Y-m-d') : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -990,9 +1017,13 @@ class Asset extends Depreciable
|
|||
* @param $value
|
||||
* @return void
|
||||
*/
|
||||
public function setRequestableAttribute($value)
|
||||
|
||||
protected function requestable(): Attribute
|
||||
{
|
||||
$this->attributes['requestable'] = (int) filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
return Attribute::make(
|
||||
get: fn ($value) => (int) filter_var($value, FILTER_VALIDATE_BOOLEAN),
|
||||
set: fn ($value) => (int) filter_var($value, FILTER_VALIDATE_BOOLEAN),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -705,18 +705,6 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
@if ($asset->expected_checkin!='')
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<strong>
|
||||
{{ trans('admin/hardware/form.expected_checkin') }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{{ Helper::getFormattedDateObject($asset->expected_checkin, 'date', false) }}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
|
@ -804,6 +792,19 @@
|
|||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@if ($asset->expected_checkin!='')
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<strong>
|
||||
{{ trans('admin/hardware/form.expected_checkin') }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{{ Helper::getFormattedDateObject($asset->expected_checkin, 'date', false) }}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($asset->last_checkin!='')
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
|
|
|
@ -36,15 +36,11 @@
|
|||
<th>{{ trans('general.error') }}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@php \Log::debug("import errors are: ".print_r($import_errors,true)); @endphp
|
||||
@foreach($import_errors AS $key => $actual_import_errors)
|
||||
@php \Log::debug("Key is: $key"); @endphp
|
||||
@foreach($actual_import_errors AS $table => $error_bag)
|
||||
@php \Log::debug("Table is: $table"); @endphp
|
||||
@foreach($error_bag as $field => $error_list)
|
||||
@php \Log::debug("Field is: $field"); @endphp
|
||||
<tr>
|
||||
<td>{{ $activeFile->file_path ?? "Unknown File" }}</td>
|
||||
<td><b>{{ $key }}</b></td>
|
||||
<td>
|
||||
<b>{{ $field }}:</b>
|
||||
<span>{{ implode(", ",$error_list) }}</span>
|
||||
|
|
|
@ -67,7 +67,7 @@ class StoreAssetTest extends TestCase
|
|||
$this->assertEquals('random_string', $asset->asset_tag);
|
||||
$this->assertEquals($userAssigned->id, $asset->assigned_to);
|
||||
$this->assertTrue($asset->company->is($company));
|
||||
$this->assertEquals('2023-09-03 00:00:00', $asset->last_audit_date->format('Y-m-d H:i:s'));
|
||||
$this->assertEquals('2023-09-03 00:00:00', $asset->last_audit_date);
|
||||
$this->assertTrue($asset->location->is($location));
|
||||
$this->assertTrue($asset->model->is($model));
|
||||
$this->assertEquals('A New Asset', $asset->name);
|
||||
|
@ -87,7 +87,7 @@ class StoreAssetTest extends TestCase
|
|||
{
|
||||
$response = $this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->postJson(route('api.assets.store'), [
|
||||
'last_audit_date' => '2023-09-03 12:23:45',
|
||||
'last_audit_date' => '2023-09-03',
|
||||
'asset_tag' => '1234',
|
||||
'model_id' => AssetModel::factory()->create()->id,
|
||||
'status_id' => Statuslabel::factory()->create()->id,
|
||||
|
@ -96,7 +96,7 @@ class StoreAssetTest extends TestCase
|
|||
->assertStatusMessageIs('success');
|
||||
|
||||
$asset = Asset::find($response['payload']['id']);
|
||||
$this->assertEquals('00:00:00', $asset->last_audit_date->format('H:i:s'));
|
||||
$this->assertEquals('2023-09-03 00:00:00', $asset->last_audit_date);
|
||||
}
|
||||
|
||||
public function testLastAuditDateCanBeNull()
|
||||
|
|
Loading…
Reference in a new issue