From 8b1e3122928a60f97c2f8141cbf137e8a60257c9 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 14 May 2026 15:33:42 +0100 Subject: [PATCH] Normalize purchase_cost --- .../Components/ComponentsController.php | 21 +++-------- .../Requests/CreateMultipleAssetRequest.php | 5 +++ app/Http/Requests/StoreAccessoryRequest.php | 6 +++- app/Http/Requests/StoreAssetRequest.php | 30 +++------------- app/Http/Requests/StoreComponentRequest.php | 27 ++++++++++++++ app/Http/Requests/StoreConsumableRequest.php | 6 +++- app/Http/Requests/UpdateAssetRequest.php | 14 ++++---- app/Http/Requests/UpdateComponentRequest.php | 35 +++++++++++++++++++ resources/lang/en-US/general.php | 3 +- resources/views/layouts/default.blade.php | 10 ++++++ resources/views/maintenances/edit.blade.php | 2 +- .../forms/edit/purchase_cost.blade.php | 2 +- 12 files changed, 108 insertions(+), 53 deletions(-) create mode 100644 app/Http/Requests/StoreComponentRequest.php create mode 100644 app/Http/Requests/UpdateComponentRequest.php diff --git a/app/Http/Controllers/Components/ComponentsController.php b/app/Http/Controllers/Components/ComponentsController.php index cd5ec0b738..90041c1857 100644 --- a/app/Http/Controllers/Components/ComponentsController.php +++ b/app/Http/Controllers/Components/ComponentsController.php @@ -4,7 +4,8 @@ namespace App\Http\Controllers\Components; use App\Helpers\Helper; use App\Http\Controllers\Controller; -use App\Http\Requests\ImageUploadRequest; +use App\Http\Requests\StoreComponentRequest; +use App\Http\Requests\UpdateComponentRequest; use App\Models\Company; use App\Models\Component; use Illuminate\Auth\Access\AuthorizationException; @@ -12,7 +13,6 @@ use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; -use Illuminate\Support\Facades\Validator; /** * This class controls all actions related to Components for @@ -74,7 +74,7 @@ class ComponentsController extends Controller * * @throws AuthorizationException */ - public function store(ImageUploadRequest $request) + public function store(StoreComponentRequest $request) { $this->authorize('create', Component::class); $component = new Component; @@ -148,21 +148,10 @@ class ComponentsController extends Controller * * @since [v3.0] */ - public function update(ImageUploadRequest $request, Component $component) + public function update(UpdateComponentRequest $request, Component $component) { - $min = $component->numCheckedOut(); - $validator = Validator::make($request->all(), [ - 'qty' => "required|numeric|min:$min", - ]); - - if ($validator->fails()) { - return redirect()->back() - ->withErrors($validator) - ->withInput(); - } - $this->authorize('update', $component); - + // Update the component data $component->name = $request->input('name'); $component->category_id = $request->input('category_id'); diff --git a/app/Http/Requests/CreateMultipleAssetRequest.php b/app/Http/Requests/CreateMultipleAssetRequest.php index 5dbf36dc05..30e40d4d81 100644 --- a/app/Http/Requests/CreateMultipleAssetRequest.php +++ b/app/Http/Requests/CreateMultipleAssetRequest.php @@ -2,6 +2,7 @@ namespace App\Http\Requests; +use App\Helpers\Helper; use App\Http\Requests\Traits\MayContainCustomFields; use App\Models\Asset; use App\Models\AssetModel; @@ -26,6 +27,10 @@ class CreateMultipleAssetRequest extends ImageUploadRequest // should I extend f { parent::prepareForValidation(); + if ($this->filled('purchase_cost') && ! is_float($this->input('purchase_cost')) && preg_match('/^[\d.,]+$/', (string) $this->input('purchase_cost'))) { + $this->merge(['purchase_cost' => Helper::ParseCurrency($this->input('purchase_cost'))]); + } + if (Setting::getSettings()->full_multiple_companies_support == '1' && ! $this->user()->isSuperUser()) { $this->mergeIfMissing(['company_id' => $this->user()->company_id]); } diff --git a/app/Http/Requests/StoreAccessoryRequest.php b/app/Http/Requests/StoreAccessoryRequest.php index afa2bf3e15..14f9faf49c 100644 --- a/app/Http/Requests/StoreAccessoryRequest.php +++ b/app/Http/Requests/StoreAccessoryRequest.php @@ -2,6 +2,7 @@ namespace App\Http\Requests; +use App\Helpers\Helper; use App\Models\Accessory; use App\Models\Category; use Illuminate\Contracts\Validation\ValidationRule; @@ -21,6 +22,10 @@ class StoreAccessoryRequest extends ImageUploadRequest { parent::prepareForValidation(); + if ($this->filled('purchase_cost') && ! is_float($this->input('purchase_cost')) && preg_match('/^[\d.,]+$/', (string) $this->input('purchase_cost'))) { + $this->merge(['purchase_cost' => Helper::ParseCurrency($this->input('purchase_cost'))]); + } + if ($this->category_id) { if ($category = Category::find($this->category_id)) { $this->merge([ @@ -28,7 +33,6 @@ class StoreAccessoryRequest extends ImageUploadRequest ]); } } - } /** diff --git a/app/Http/Requests/StoreAssetRequest.php b/app/Http/Requests/StoreAssetRequest.php index 6ff8d51eb9..069d4955f4 100644 --- a/app/Http/Requests/StoreAssetRequest.php +++ b/app/Http/Requests/StoreAssetRequest.php @@ -2,10 +2,10 @@ namespace App\Http\Requests; +use App\Helpers\Helper; use App\Http\Requests\Traits\MayContainCustomFields; use App\Models\Asset; use App\Models\Company; -use App\Models\Setting; use App\Rules\AssetCannotBeCheckedOutToNondeployableStatus; use Carbon\Carbon; use Carbon\Exceptions\InvalidFormatException; @@ -39,6 +39,9 @@ class StoreAssetRequest extends ImageUploadRequest $this->merge([ 'asset_tag' => $this->asset_tag ?? Asset::autoincrement_asset(), 'company_id' => $idForCurrentUser, + 'purchase_cost' => $this->filled('purchase_cost') && ! is_float($this->input('purchase_cost')) && preg_match('/^[\d.,]+$/', (string) $this->input('purchase_cost')) + ? Helper::ParseCurrency($this->input('purchase_cost')) + : $this->input('purchase_cost'), ]); } @@ -49,15 +52,6 @@ class StoreAssetRequest extends ImageUploadRequest { $modelRules = (new Asset)->getRules(); - if (Setting::getSettings()->digit_separator === '1.234,56' && is_string($this->input('purchase_cost'))) { - // If purchase_cost was submitted as a string with a comma separator - // then we need to ignore the normal numeric rules. - // Since the original rules still live on the model they will be run - // right before saving (and after purchase_cost has been - // converted to a float via setPurchaseCostAttribute). - $modelRules = $this->removeNumericRulesFromPurchaseCost($modelRules); - } - return array_merge( $modelRules, ['status_id' => [new AssetCannotBeCheckedOutToNondeployableStatus]], @@ -81,20 +75,4 @@ class StoreAssetRequest extends ImageUploadRequest } } } - - private function removeNumericRulesFromPurchaseCost(array $rules): array - { - $purchaseCost = $rules['purchase_cost']; - - // If rule is in "|" format then turn it into an array - if (is_string($purchaseCost)) { - $purchaseCost = explode('|', $purchaseCost); - } - - $rules['purchase_cost'] = array_filter($purchaseCost, function ($rule) { - return $rule !== 'numeric' && $rule !== 'gte:0'; - }); - - return $rules; - } } diff --git a/app/Http/Requests/StoreComponentRequest.php b/app/Http/Requests/StoreComponentRequest.php new file mode 100644 index 0000000000..8f3867d557 --- /dev/null +++ b/app/Http/Requests/StoreComponentRequest.php @@ -0,0 +1,27 @@ +filled('purchase_cost') && ! is_float($this->input('purchase_cost')) && preg_match('/^[\d.,]+$/', (string) $this->input('purchase_cost'))) { + $this->merge(['purchase_cost' => Helper::ParseCurrency($this->input('purchase_cost'))]); + } + } + + public function response(array $errors) + { + return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag); + } +} diff --git a/app/Http/Requests/StoreConsumableRequest.php b/app/Http/Requests/StoreConsumableRequest.php index cfe2fccae9..9cb12c6ae4 100644 --- a/app/Http/Requests/StoreConsumableRequest.php +++ b/app/Http/Requests/StoreConsumableRequest.php @@ -2,6 +2,7 @@ namespace App\Http\Requests; +use App\Helpers\Helper; use App\Models\Category; use App\Models\Consumable; use Illuminate\Contracts\Validation\ValidationRule; @@ -21,6 +22,10 @@ class StoreConsumableRequest extends ImageUploadRequest { parent::prepareForValidation(); + if ($this->filled('purchase_cost') && ! is_float($this->input('purchase_cost')) && preg_match('/^[\d.,]+$/', (string) $this->input('purchase_cost'))) { + $this->merge(['purchase_cost' => Helper::ParseCurrency($this->input('purchase_cost'))]); + } + if ($this->category_id) { if ($category = Category::find($this->category_id)) { $this->merge([ @@ -28,7 +33,6 @@ class StoreConsumableRequest extends ImageUploadRequest ]); } } - } /** diff --git a/app/Http/Requests/UpdateAssetRequest.php b/app/Http/Requests/UpdateAssetRequest.php index c8f258ed45..ad7e8ea01d 100644 --- a/app/Http/Requests/UpdateAssetRequest.php +++ b/app/Http/Requests/UpdateAssetRequest.php @@ -2,6 +2,7 @@ namespace App\Http\Requests; +use App\Helpers\Helper; use App\Http\Requests\Traits\MayContainCustomFields; use App\Models\Asset; use App\Models\Setting; @@ -22,6 +23,13 @@ class UpdateAssetRequest extends ImageUploadRequest return Gate::allows('update', $this->asset); } + public function prepareForValidation(): void + { + if ($this->filled('purchase_cost') && ! is_float($this->input('purchase_cost')) && preg_match('/^[\d.,]+$/', (string) $this->input('purchase_cost'))) { + $this->merge(['purchase_cost' => Helper::ParseCurrency($this->input('purchase_cost'))]); + } + } + /** * Get the validation rules that apply to the request. * @@ -51,12 +59,6 @@ class UpdateAssetRequest extends ImageUploadRequest ], ); - // if the purchase cost is passed in as a string **and** the digit_separator is ',' (as is common in the EU) - // then we tweak the purchase_cost rule to make it a string - if ($setting->digit_separator === '1.234,56' && is_string($this->input('purchase_cost'))) { - $rules['purchase_cost'] = ['nullable', 'string']; - } - return $rules; } } diff --git a/app/Http/Requests/UpdateComponentRequest.php b/app/Http/Requests/UpdateComponentRequest.php new file mode 100644 index 0000000000..01132bd4f9 --- /dev/null +++ b/app/Http/Requests/UpdateComponentRequest.php @@ -0,0 +1,35 @@ +component); + } + + public function prepareForValidation(): void + { + if ($this->filled('purchase_cost') && ! is_float($this->input('purchase_cost')) && preg_match('/^[\d.,]+$/', (string) $this->input('purchase_cost'))) { + $this->merge(['purchase_cost' => Helper::ParseCurrency($this->input('purchase_cost'))]); + } + } + + public function rules(): array + { + $min = $this->component->numCheckedOut(); + + return array_merge(parent::rules(), [ + 'qty' => "required|numeric|min:{$min}", + ]); + } + + public function response(array $errors) + { + return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag); + } +} diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php index de0480d309..af59ead740 100644 --- a/resources/lang/en-US/general.php +++ b/resources/lang/en-US/general.php @@ -255,7 +255,8 @@ return [ 'processing' => 'Processing', 'profile' => 'View Profile', 'purchase_cost' => 'Purchase Cost', - 'purchase_cost_format_help' => 'Use your configured number format, e.g. :format', + 'purchase_cost_format_help' => 'This should be entered in your system-configured number format, e.g. :format', + 'purchase_cost_invalid' => 'Please enter a valid number (digits, dots, and commas only)', 'purchase_date' => 'Purchase Date', 'qty' => 'QTY', 'quantity' => 'Quantity', diff --git a/resources/views/layouts/default.blade.php b/resources/views/layouts/default.blade.php index 5ae1cc0a6f..c6a38cd196 100644 --- a/resources/views/layouts/default.blade.php +++ b/resources/views/layouts/default.blade.php @@ -2344,6 +2344,16 @@ email: "{{ trans('validation.generic.email') }}" }); + $.validator.addMethod('pattern', function(value, element, param) { + if (this.optional(element)) { + return true; + } + if (typeof param === 'string') { + param = new RegExp('^(?:' + param + ')$'); + } + return param.test(value); + }, '{{ trans('validation.generic.invalid_value_in_field') }}'); + function showHideEncValue(e) { // Use element id to find the text element to hide / show diff --git a/resources/views/maintenances/edit.blade.php b/resources/views/maintenances/edit.blade.php index 1aecbd6139..f5dd76a2b6 100644 --- a/resources/views/maintenances/edit.blade.php +++ b/resources/views/maintenances/edit.blade.php @@ -166,7 +166,7 @@ {{ $snipeSettings->default_currency }} @endif - + {!! $errors->first('cost', '') !!}

{{ trans('general.purchase_cost_format_help', ['format' => $snipeSettings->digit_separator]) }}

diff --git a/resources/views/partials/forms/edit/purchase_cost.blade.php b/resources/views/partials/forms/edit/purchase_cost.blade.php index 3ba6a40765..7a4cc1f4e7 100644 --- a/resources/views/partials/forms/edit/purchase_cost.blade.php +++ b/resources/views/partials/forms/edit/purchase_cost.blade.php @@ -3,7 +3,7 @@
- + @if (isset($currency_type)) {{ $currency_type }}