mirror of
https://github.com/snipe/snipe-it.git
synced 2026-02-05 03:05:41 +00:00
Merge pull request #18247 from uberbrady/multi_create_fixes
Fixed #18160 - Multi-create fixes
This commit is contained in:
@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Requests\CreateMultipleAssetRequest;
|
||||
use App\Http\Requests\UpdateAssetRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
@ -98,7 +99,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function store(ImageUploadRequest $request) : RedirectResponse
|
||||
public function store(CreateMultipleAssetRequest $request): RedirectResponse
|
||||
{
|
||||
$this->authorize(Asset::class);
|
||||
|
||||
@ -135,122 +136,136 @@ class AssetsController extends Controller
|
||||
$successes = [];
|
||||
$failures = [];
|
||||
|
||||
for ($a = 1, $aMax = count($asset_tags); $a <= $aMax; $a++) {
|
||||
$asset = new Asset();
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
for ($a = 1, $aMax = count($asset_tags); $a <= $aMax; $a++) {
|
||||
$asset = new Asset();
|
||||
|
||||
$asset->model()->associate($model);
|
||||
$asset->name = $request->input('name');
|
||||
$asset->model()->associate($model);
|
||||
$asset->name = $request->input('name');
|
||||
|
||||
// Check for a corresponding serial
|
||||
if (($serials) && (array_key_exists($a, $serials))) {
|
||||
$asset->serial = $serials[$a];
|
||||
}
|
||||
|
||||
if (($asset_tags) && (array_key_exists($a, $asset_tags))) {
|
||||
$asset->asset_tag = $asset_tags[$a];
|
||||
}
|
||||
|
||||
$asset->company_id = $companyId;
|
||||
$asset->model_id = $request->input('model_id');
|
||||
$asset->order_number = $request->input('order_number');
|
||||
$asset->notes = $request->input('notes');
|
||||
$asset->created_by = auth()->id();
|
||||
$asset->status_id = request('status_id');
|
||||
$asset->warranty_months = request('warranty_months', null);
|
||||
$asset->purchase_cost = request('purchase_cost');
|
||||
$asset->purchase_date = request('purchase_date', null);
|
||||
$asset->asset_eol_date = request('asset_eol_date', null);
|
||||
$asset->assigned_to = request('assigned_to', null);
|
||||
$asset->supplier_id = request('supplier_id', null);
|
||||
$asset->requestable = request('requestable', 0);
|
||||
$asset->rtd_location_id = request('rtd_location_id', null);
|
||||
$asset->byod = request('byod', 0);
|
||||
|
||||
if (! empty($settings->audit_interval)) {
|
||||
$asset->next_audit_date = Carbon::now()->addMonths((int) $settings->audit_interval)->toDateString();
|
||||
}
|
||||
|
||||
// Set location_id to rtd_location_id ONLY if the asset isn't being checked out
|
||||
if (!request('assigned_user') && !request('assigned_asset') && !request('assigned_location')) {
|
||||
$asset->location_id = $request->input('rtd_location_id', null);
|
||||
}
|
||||
|
||||
if ($request->has('use_cloned_image')) {
|
||||
$cloned_model_img = Asset::select('image')->find($request->input('clone_image_from_id'));
|
||||
if ($cloned_model_img) {
|
||||
$new_image_name = 'clone-'.date('U').'-'.$cloned_model_img->image;
|
||||
$new_image = 'assets/'.$new_image_name;
|
||||
Storage::disk('public')->copy('assets/'.$cloned_model_img->image, $new_image);
|
||||
$asset->image = $new_image_name;
|
||||
// Check for a corresponding serial
|
||||
if (($serials) && (array_key_exists($a, $serials))) {
|
||||
$asset->serial = $serials[$a];
|
||||
}
|
||||
|
||||
} else {
|
||||
$asset = $request->handleImages($asset);
|
||||
}
|
||||
if (($asset_tags) && (array_key_exists($a, $asset_tags))) {
|
||||
$asset->asset_tag = $asset_tags[$a];
|
||||
}
|
||||
|
||||
// Update custom fields in the database.
|
||||
// Validation for these fields is handled through the AssetRequest form request
|
||||
$asset->company_id = $companyId;
|
||||
$asset->model_id = $request->input('model_id');
|
||||
$asset->order_number = $request->input('order_number');
|
||||
$asset->notes = $request->input('notes');
|
||||
$asset->created_by = auth()->id();
|
||||
$asset->status_id = request('status_id');
|
||||
$asset->warranty_months = request('warranty_months', null);
|
||||
$asset->purchase_cost = request('purchase_cost');
|
||||
$asset->purchase_date = request('purchase_date', null);
|
||||
$asset->asset_eol_date = request('asset_eol_date', null);
|
||||
$asset->assigned_to = request('assigned_to', null);
|
||||
$asset->supplier_id = request('supplier_id', null);
|
||||
$asset->requestable = request('requestable', 0);
|
||||
$asset->rtd_location_id = request('rtd_location_id', null);
|
||||
$asset->byod = request('byod', 0);
|
||||
|
||||
if (($model) && ($model->fieldset)) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
if (!empty($settings->audit_interval)) {
|
||||
$asset->next_audit_date = Carbon::now()->addMonths((int)$settings->audit_interval)->toDateString();
|
||||
}
|
||||
|
||||
// Set location_id to rtd_location_id ONLY if the asset isn't being checked out
|
||||
if (!request('assigned_user') && !request('assigned_asset') && !request('assigned_location')) {
|
||||
$asset->location_id = $request->input('rtd_location_id', null);
|
||||
}
|
||||
|
||||
if ($request->has('use_cloned_image')) {
|
||||
$cloned_model_img = Asset::select('image')->find($request->input('clone_image_from_id'));
|
||||
if ($cloned_model_img) {
|
||||
$new_image_name = 'clone-' . date('U') . '-' . $cloned_model_img->image;
|
||||
$new_image = 'assets/' . $new_image_name;
|
||||
Storage::disk('public')->copy('assets/' . $cloned_model_img->image, $new_image);
|
||||
$asset->image = $new_image_name;
|
||||
}
|
||||
|
||||
} else {
|
||||
$asset = $request->handleImages($asset);
|
||||
}
|
||||
|
||||
// Update custom fields in the database.
|
||||
// Validation for these fields is handled through the AssetRequest form request
|
||||
|
||||
if (($model) && ($model->fieldset)) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
} else {
|
||||
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
||||
} else {
|
||||
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
||||
} else {
|
||||
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the asset before saving
|
||||
// Note - it can be tempting to instead want to call saveOrFail(), to automatically throw when an object
|
||||
// is invalid (and can't save). But this won't work, because Custom Fields _overrides_ the save() method
|
||||
// to inject the Custom Field Rules into the $rules property right before invoking the _real_ save.
|
||||
// so, instead, we have to catch failures on the 'else' clause and throw there.
|
||||
if ($asset->isValid() && $asset->save()) {
|
||||
$target = null;
|
||||
$location = null;
|
||||
|
||||
if ($userId = request('assigned_user')) {
|
||||
$target = User::find($userId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.user'));
|
||||
}
|
||||
$location = $target->location_id;
|
||||
|
||||
} elseif ($assetId = request('assigned_asset')) {
|
||||
$target = Asset::find($assetId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.asset'));
|
||||
}
|
||||
$location = $target->location_id;
|
||||
|
||||
} elseif ($locationId = request('assigned_location')) {
|
||||
$target = Location::find($locationId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.location'));
|
||||
}
|
||||
$location = $target->id;
|
||||
}
|
||||
|
||||
if (isset($target)) {
|
||||
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', $request->get('name'), $location);
|
||||
}
|
||||
|
||||
$successes[] = "<a href='" . route('hardware.show', $asset) . "' style='color: white;'>" . e($asset->asset_tag) . "</a>";
|
||||
|
||||
} else {
|
||||
$asset->throwValidationException(); // we have to do this for the reason listed above - can't use saveOrFail()
|
||||
$failures[] = join(",", $asset->getErrors()->all()); //TODO - this can probably go away soon
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the asset before saving
|
||||
if ($asset->isValid() && $asset->save()) {
|
||||
$target = null;
|
||||
$location = null;
|
||||
|
||||
if ($userId = request('assigned_user')) {
|
||||
$target = User::find($userId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.user'));
|
||||
}
|
||||
$location = $target->location_id;
|
||||
|
||||
} elseif ($assetId = request('assigned_asset')) {
|
||||
$target = Asset::find($assetId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.asset'));
|
||||
}
|
||||
$location = $target->location_id;
|
||||
|
||||
} elseif ($locationId = request('assigned_location')) {
|
||||
$target = Location::find($locationId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.location'));
|
||||
}
|
||||
$location = $target->id;
|
||||
}
|
||||
|
||||
if (isset($target)) {
|
||||
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', $request->get('name'), $location);
|
||||
}
|
||||
|
||||
$successes[] = "<a href='" . route('hardware.show', $asset) . "' style='color: white;'>" . e($asset->asset_tag) . "</a>";
|
||||
|
||||
} else {
|
||||
$failures[] = join(",", $asset->getErrors()->all());
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
\Log::debug("Caught exception in multi-create - rolling back: " . $e->getMessage());
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
DB::commit();
|
||||
|
||||
if($request->get('redirect_option') === 'back'){
|
||||
session()->put(['redirect_option' => 'index']);
|
||||
} else {
|
||||
|
||||
68
app/Http/Requests/CreateMultipleAssetRequest.php
Normal file
68
app/Http/Requests/CreateMultipleAssetRequest.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Http\Requests\Traits\MayContainCustomFields;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Setting;
|
||||
use App\Models\AssetModel;
|
||||
use App\Rules\UniqueUndeleted;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CreateMultipleAssetRequest extends ImageUploadRequest //should I extend from StoreAssetRequest? FIXME OR TODO OR THINKME
|
||||
{
|
||||
use MayContainCustomFields;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true; //TODO - should I do the auth check here?
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
//grab the rules for serials and asset_tags, and tweak them into an array context for multi-create usage
|
||||
$modelRules = (new Asset)->getRules();
|
||||
unset($modelRules['serial']);
|
||||
|
||||
$asset_tag_rules = $modelRules['asset_tag'];
|
||||
unset($modelRules['asset_tag']);
|
||||
// now we replace the 'not_array' rule with 'distinct'
|
||||
array_splice($asset_tag_rules, array_search('not_array', $asset_tag_rules), 1, 'distinct');
|
||||
// and replace the 'unique_undeleted' rule with the Rule object
|
||||
foreach ($asset_tag_rules as $i => $asset_tag_rule) {
|
||||
if (Str::startsWith($asset_tag_rule, 'unique_undeleted')) {
|
||||
$asset_tag_rules[$i] = new UniqueUndeleted('assets', 'asset_tag');
|
||||
}
|
||||
}
|
||||
|
||||
$serials_unique = Setting::getSettings()['unique_serial'];
|
||||
$serials_required = AssetModel::find($this?->model_id)?->require_serial;
|
||||
|
||||
$serial_rules = ['string'];
|
||||
if ($serials_unique) {
|
||||
// $serial_rules[] = 'unique_undeleted:assets,serial';
|
||||
$serial_rules[] = new UniqueUndeleted('assets', 'serial');
|
||||
$serial_rules[] = 'distinct';
|
||||
}
|
||||
if ($serials_required) {
|
||||
$serial_rules[] = 'required';
|
||||
} else {
|
||||
$serial_rules[] = 'nullable';
|
||||
}
|
||||
|
||||
return array_merge($modelRules, [
|
||||
'serials.*' => $serial_rules,
|
||||
'asset_tags.*' => $asset_tag_rules,
|
||||
]);
|
||||
}
|
||||
}
|
||||
56
app/Rules/UniqueUndeleted.php
Normal file
56
app/Rules/UniqueUndeleted.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Contracts\Validation\ValidatorAwareRule;
|
||||
use Illuminate\Validation\Validator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Contracts\Validation\DataAwareRule;
|
||||
|
||||
class UniqueUndeleted implements ValidationRule, ValidatorAwareRule
|
||||
{
|
||||
protected ?Validator $validator = null;
|
||||
protected array $columns = [];
|
||||
protected $data = [];
|
||||
|
||||
public function __construct(
|
||||
public string $table,
|
||||
string ...$columns,
|
||||
)
|
||||
{
|
||||
$this->columns = $columns;
|
||||
}
|
||||
|
||||
public function setValidator(Validator $validator): static
|
||||
{
|
||||
$this->validator = $validator;
|
||||
$this->data = $validator->getData();
|
||||
//TODO - can we somehow grab the ID of the route-model-bound object, and omit its ID?
|
||||
// to do that, we'd have to know _which_ parameter in the validator is actually the R-M-B'ed
|
||||
// parameter. Or maybe we just change the function signature to let you specify it.
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$query = DB::table($this->table)->whereNull('deleted_at');
|
||||
$query->where($this->columns[0], '=', $value); //the first column to check
|
||||
$translation_string = 'validation.unique_undeleted'; //the normal validation string for a single-column check
|
||||
foreach (array_slice($this->columns, 1) as $column) {
|
||||
$translation_string = 'validation.two_column_unique_undeleted';
|
||||
$query->where($column, '=', $this->data[$column]);
|
||||
}
|
||||
|
||||
if ($query->count() > 0) {
|
||||
$fail($translation_string)->translate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -230,7 +230,10 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'attributes' => [],
|
||||
'attributes' => [
|
||||
'serials.*' => 'Serial Number',
|
||||
'asset_tags.*' => 'Asset Tag',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
|
||||
<!-- Asset Tag -->
|
||||
<div class="form-group {{ $errors->has('asset_tag') ? ' has-error' : '' }}">
|
||||
<div class="form-group {{ ($errors->has('asset_tag') || $errors->has('asset_tags.1')) ? ' has-error' : '' }}">
|
||||
<label for="asset_tag" class="col-md-3 control-label">{{ trans('admin/hardware/form.tag') }}</label>
|
||||
|
||||
|
||||
@ -39,8 +39,10 @@
|
||||
@else
|
||||
<!-- we are creating a new asset - let people use more than one asset tag -->
|
||||
<div class="col-md-7 col-sm-12">
|
||||
<input class="form-control" type="text" name="asset_tags[1]" id="asset_tag" value="{{ old('asset_tags.1', \App\Models\Asset::autoincrement_asset()) }}" required>
|
||||
{!! $errors->first('asset_tags', '<span class="alert-msg"><i class="fas fa-times"></i> :message</span>') !!}
|
||||
<input class="form-control"
|
||||
type="text" name="asset_tags[1]" id="asset_tag"
|
||||
value="{{ old('asset_tags.1', \App\Models\Asset::autoincrement_asset()) }}" required>
|
||||
{!! $errors->first('asset_tags.1', '<span class="alert-msg"><i class="fas fa-times"></i> :message</span>') !!}
|
||||
{!! $errors->first('asset_tag', '<span class="alert-msg"><i class="fas fa-times"></i> :message</span>') !!}
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-12">
|
||||
@ -57,6 +59,39 @@
|
||||
@include ('partials.forms.edit.serial', ['fieldname'=> 'serials[1]', 'old_val_name' => 'serials.1', 'translated_serial' => trans('admin/hardware/form.serial')])
|
||||
|
||||
<div class="input_fields_wrap">
|
||||
{{-- If we're back on this screen for an error, *and* we are doing 'create multiple', then... --}}
|
||||
@php
|
||||
$old_tags = old('asset_tags',[]);
|
||||
/**
|
||||
okay, so old() comes back as:
|
||||
(
|
||||
[1] => 1744410541
|
||||
[2] => 1744410542
|
||||
)
|
||||
*/
|
||||
if(array_key_exists('1',$old_tags)) {
|
||||
unset($old_tags[1]); //we already handled 'asset_tag.1' - so unset it
|
||||
}
|
||||
@endphp
|
||||
@foreach(array_keys($old_tags) as $i)
|
||||
{{-- This is mostly stolen from the HTML that we add via javascript on the 'add_field_button' handler in the embedded JS below --}}
|
||||
{{-- @include ('partials.forms.edit.serial', ['fieldname'=> 'serials['.$loop->iteration.']', 'old_val_name' => 'serials.'.$loop->iteration, 'translated_serial' => trans('admin/hardware/form.serial')])--}}
|
||||
<span class="fields_wrapper">
|
||||
<div class="form-group {{ $errors->has('asset_tags.'.$i) ? ' has-error' : '' }}"><label for="asset_tag"
|
||||
class="col-md-3 control-label">{{ trans('admin/hardware/form.tag') }} {{ $i }}</label>
|
||||
<div class="col-md-7 col-sm-12 required">
|
||||
<input type="text" class="form-control" name="asset_tags[{{ $i }}]"
|
||||
value="{{ old('asset_tags.'.$i) }}"
|
||||
required>
|
||||
{!! $errors->first('asset_tags.'.$i, '<span class="alert-msg"><i class="fas fa-times"></i> :message</span>') !!}
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-12">
|
||||
<a href="#" class="remove_field btn btn-default btn-sm"><x-icon type="minus"/></a>
|
||||
</div>
|
||||
</div>
|
||||
@include ('partials.forms.edit.serial', ['fieldname'=> 'serials['.$i.']', 'old_val_name' => 'serials.'.$i, 'translated_serial' => trans('admin/hardware/form.serial')])
|
||||
</span>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@include ('partials.forms.edit.model-select', ['translated_name' => trans('admin/hardware/form.model'), 'fieldname' => 'model_id', 'field_req' => true])
|
||||
@ -290,7 +325,7 @@
|
||||
var max_fields = 100; //maximum input boxes allowed
|
||||
var wrapper = $(".input_fields_wrap"); //Fields wrapper
|
||||
var add_button = $(".add_field_button"); //Add button ID
|
||||
var x = 1; //initial text box count
|
||||
var x = {{ old('asset_tags') ? count(old('asset_tags')) : 1 /* If we have old() data, use that to determine the 'next' number for 'Asset Tag 2' etc. Otherwise, use 1 */ }}; //initial text box count
|
||||
|
||||
|
||||
|
||||
@ -314,6 +349,8 @@
|
||||
|
||||
x++; //text box increment
|
||||
|
||||
// NOTE - this is duplicated in the blade itself in order to re-display an attempt to insert multiple assets
|
||||
// So if this changes, that needs to change too.
|
||||
box_html += '<span class="fields_wrapper">';
|
||||
box_html += '<div class="form-group"><label for="asset_tag" class="col-md-3 control-label">{{ trans('admin/hardware/form.tag') }} ' + x + '</label>';
|
||||
box_html += '<div class="col-md-7 col-sm-12 required">';
|
||||
@ -322,7 +359,7 @@
|
||||
box_html += '<div class="col-md-2 col-sm-12">';
|
||||
box_html += '<a href="#" class="remove_field btn btn-default btn-sm"><x-icon type="minus" /></a>';
|
||||
box_html += '</div>';
|
||||
box_html += '</div>';
|
||||
// box_html += '</div>';
|
||||
box_html += '</div>';
|
||||
box_html += '<div class="form-group"><label for="serial" class="col-md-3 control-label">{{ trans('admin/hardware/form.serial') }} ' + x + '</label>';
|
||||
box_html += '<div class="col-md-7 col-sm-12">';
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!-- Serial -->
|
||||
<div class="form-group {{ $errors->has('serial') ? ' has-error' : '' }}">
|
||||
<div class="form-group {{ $errors->has($old_val_name ?? $fieldname) ? ' has-error' : '' }}">
|
||||
<label for="{{ $fieldname }}" class="col-md-3 control-label">{{ trans('admin/hardware/form.serial') }} </label>
|
||||
<div class="col-md-7 col-sm-12">
|
||||
<input class="form-control" type="text" name="{{ $fieldname }}" id="{{ $fieldname }}" value="{{ old((isset($old_val_name) ? $old_val_name : $fieldname), $item->serial) }}" {{ (Helper::checkIfRequired($item, 'serial') || ($item->model && $item->model->require_serial)) ? ' required' : '' }} maxlength="191" />
|
||||
|
||||
Reference in New Issue
Block a user