Normalize purchase_cost

This commit is contained in:
snipe
2026-05-14 15:33:42 +01:00
parent 9004211a59
commit 8b1e312292
12 changed files with 108 additions and 53 deletions
@@ -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');
@@ -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]);
}
+5 -1
View File
@@ -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
]);
}
}
}
/**
+4 -26
View File
@@ -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;
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Requests;
use App\Helpers\Helper;
use App\Models\Component;
use Illuminate\Support\Facades\Gate;
class StoreComponentRequest extends ImageUploadRequest
{
public function authorize(): bool
{
return Gate::allows('create', Component::class);
}
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 response(array $errors)
{
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
}
}
+5 -1
View File
@@ -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
]);
}
}
}
/**
+8 -6
View File
@@ -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;
}
}
@@ -0,0 +1,35 @@
<?php
namespace App\Http\Requests;
use App\Helpers\Helper;
use Illuminate\Support\Facades\Gate;
class UpdateComponentRequest extends ImageUploadRequest
{
public function authorize(): bool
{
return Gate::allows('update', $this->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);
}
}
+2 -1
View File
@@ -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',
+10
View File
@@ -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
+1 -1
View File
@@ -166,7 +166,7 @@
{{ $snipeSettings->default_currency }}
@endif
</span>
<input class="form-control" type="text" inputmode="decimal" pattern="^\d+([.,]\d+)?$" name="cost" aria-label="cost" id="cost" value="{{ old('cost', $item->cost) }}" maxlength="25"/>
<input class="form-control" type="text" inputmode="decimal" pattern="[\d.,]+" name="cost" aria-label="cost" id="cost" value="{{ old('cost', \App\Helpers\Helper::formatCurrencyOutput($item->cost)) }}" maxlength="25" data-msg-pattern="{{ trans('general.purchase_cost_invalid') }}"/>
{!! $errors->first('cost', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
<p class="help-block">{{ trans('general.purchase_cost_format_help', ['format' => $snipeSettings->digit_separator]) }}</p>
</div>
@@ -3,7 +3,7 @@
<label for="purchase_cost" class="col-md-3 control-label">{{ $unit_cost ?? trans('general.purchase_cost') }}</label>
<div class="col-md-9">
<div class="input-group col-md-5" style="padding-left: 0px;">
<input class="form-control" type="text" name="purchase_cost" pattern="^\d+([.,]\d+)?$" aria-label="purchase_cost" id="purchase_cost" value="{{ old('purchase_cost', $item->purchase_cost) }}" maxlength="25" inputmode="decimal"/>
<input class="form-control" type="text" name="purchase_cost" pattern="[\d.,]+" aria-label="purchase_cost" id="purchase_cost" value="{{ old('purchase_cost', \App\Helpers\Helper::formatCurrencyOutput($item->purchase_cost)) }}" maxlength="25" inputmode="decimal" data-msg-pattern="{{ trans('general.purchase_cost_invalid') }}"/>
<span class="input-group-addon">
@if (isset($currency_type))
{{ $currency_type }}