mirror of
https://github.com/snipe/snipe-it.git
synced 2024-11-12 16:44:08 -08:00
Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net> # Conflicts: # .all-contributorsrc # CONTRIBUTORS.md
This commit is contained in:
commit
1a541ce220
|
@ -3137,6 +3137,15 @@
|
|||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "FlorentDotMe",
|
||||
"name": "Florent Bervas",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/292081?v=4",
|
||||
"profile": "http://spoontux.net",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Galaxy102",
|
||||
"name": "Konstantin Köhring",
|
||||
|
@ -3146,5 +3155,6 @@
|
|||
"code"
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
|||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/197404?v=3" width="110px;"/><br /><sub>snipe</sub>](http://www.snipe.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=snipe "Code") [🚇](#infra-snipe "Infrastructure (Hosting, Build-Tools, etc)") [📖](https://github.com/snipe/snipe-it/commits?author=snipe "Documentation") [⚠️](https://github.com/snipe/snipe-it/commits?author=snipe "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Asnipe "Bug reports") [🎨](#design-snipe "Design") [👀](#review-snipe "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/36335?v=3" width="110px;"/><br /><sub>Brady Wetherington</sub>](http://www.uberbrady.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=uberbrady "Code") [📖](https://github.com/snipe/snipe-it/commits?author=uberbrady "Documentation") [🚇](#infra-uberbrady "Infrastructure (Hosting, Build-Tools, etc)") [👀](#review-uberbrady "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/3803132?v=3" width="110px;"/><br /><sub>Daniel Meltzer</sub>](https://github.com/dmeltzer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Tests") [📖](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/1609106?v=3" width="110px;"/><br /><sub>Michael T</sub>](http://www.tuckertechonline.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mtucker6784 "Code") | [<img src="https://avatars2.githubusercontent.com/u/3274937?v=3" width="110px;"/><br /><sub>madd15</sub>](https://github.com/madd15)<br />[📖](https://github.com/snipe/snipe-it/commits?author=madd15 "Documentation") [💬](#question-madd15 "Answering Questions") | [<img src="https://avatars2.githubusercontent.com/u/894126?v=3" width="110px;"/><br /><sub>Vincent Sposato</sub>](https://github.com/vsposato)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vsposato "Code") | [<img src="https://avatars0.githubusercontent.com/u/1639757?v=3" width="110px;"/><br /><sub>Andrea Bergamasco</sub>](https://github.com/vjandrea)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vjandrea "Code") |
|
||||
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
||||
| :---: | :---: | :---: |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| :---: | :---: | :---: |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/10640152?v=3" width="110px;"/><br /><sub>Karol</sub>](https://github.com/kpawelski)<br />[🌍](#translation-kpawelski "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=kpawelski "Code") | [<img src="https://avatars3.githubusercontent.com/u/600106?v=3" width="110px;"/><br /><sub>morph027</sub>](http://blog.morph027.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=morph027 "Code") | [<img src="https://avatars3.githubusercontent.com/u/22935755?v=3" width="110px;"/><br /><sub>fvleminckx</sub>](https://github.com/fvleminckx)<br />[🚇](#infra-fvleminckx "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars2.githubusercontent.com/u/15633547?v=3" width="110px;"/><br /><sub>itsupportcmsukorg</sub>](https://github.com/itsupportcmsukorg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=itsupportcmsukorg "Code") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aitsupportcmsukorg "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/12373799?v=3" width="110px;"/><br /><sub>Frank</sub>](https://override.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=base-zero "Code") | [<img src="https://avatars0.githubusercontent.com/u/10137?v=3" width="110px;"/><br /><sub>Deleted user</sub>](https://github.com/ghost)<br />[🌍](#translation-ghost "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=ghost "Code") | [<img src="https://avatars1.githubusercontent.com/u/10802313?v=3" width="110px;"/><br /><sub>tiagom62</sub>](https://github.com/tiagom62)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tiagom62 "Code") [🚇](#infra-tiagom62 "Infrastructure (Hosting, Build-Tools, etc)") |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/2389047?v=3" width="110px;"/><br /><sub>Ryan Stafford</sub>](https://github.com/rystaf)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rystaf "Code") | [<img src="https://avatars2.githubusercontent.com/u/10345935?v=3" width="110px;"/><br /><sub>Eammon Hanlon</sub>](https://github.com/ehanlon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ehanlon "Code") | [<img src="https://avatars0.githubusercontent.com/u/441924?v=3" width="110px;"/><br /><sub>zjean</sub>](https://github.com/zjean)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zjean "Code") | [<img src="https://avatars0.githubusercontent.com/u/12660103?v=3" width="110px;"/><br /><sub>Matthias Frei</sub>](http://www.frei.media)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FREImedia "Code") | [<img src="https://avatars0.githubusercontent.com/u/3767518?v=3" width="110px;"/><br /><sub>opsydev</sub>](https://github.com/opsydev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=opsydev "Code") | [<img src="https://avatars1.githubusercontent.com/u/82290?v=3" width="110px;"/><br /><sub>Daniel Dreier</sub>](http://www.ddreier.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ddreier "Code") | [<img src="https://avatars0.githubusercontent.com/u/23448?v=3" width="110px;"/><br /><sub>Nikolai Prokoschenko</sub>](http://rassie.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rassie "Code") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/13452757?v=3" width="110px;"/><br /><sub>Drew</sub>](https://github.com/YetAnotherCodeMonkey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=YetAnotherCodeMonkey "Code") | [<img src="https://avatars0.githubusercontent.com/u/1342320?v=3" width="110px;"/><br /><sub>Walter</sub>](https://github.com/merid14)<br />[💻](https://github.com/snipe/snipe-it/commits?author=merid14 "Code") | [<img src="https://avatars3.githubusercontent.com/u/11254614?v=3" width="110px;"/><br /><sub>Petr Baloun</sub>](https://github.com/balous)<br />[💻](https://github.com/snipe/snipe-it/commits?author=balous "Code") | [<img src="https://avatars0.githubusercontent.com/u/6117660?v=3" width="110px;"/><br /><sub>reidblomquist</sub>](https://github.com/reidblomquist)<br />[📖](https://github.com/snipe/snipe-it/commits?author=reidblomquist "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/539914?v=3" width="110px;"/><br /><sub>Mathieu Kooiman</sub>](https://github.com/mathieuk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mathieuk "Code") | [<img src="https://avatars3.githubusercontent.com/u/6606421?v=3" width="110px;"/><br /><sub>csayre</sub>](https://github.com/csayre)<br />[📖](https://github.com/snipe/snipe-it/commits?author=csayre "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/768488?v=3" width="110px;"/><br /><sub>Adam Dunson</sub>](https://github.com/adamdunson)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamdunson "Code") |
|
||||
|
@ -51,7 +51,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
|||
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/18245993?v=4" width="110px;"/><br /><sub>Konstantin Köhring</sub>](https://www.galaxy102.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Galaxy102 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/18245993?v=4" width="110px;"/><br /><sub>Konstantin Köhring</sub>](https://www.galaxy102.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Galaxy102 "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api;
|
|||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\StoreConsumableRequest;
|
||||
use App\Http\Transformers\ConsumablesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Company;
|
||||
|
@ -27,27 +28,8 @@ class ConsumablesController extends Controller
|
|||
{
|
||||
$this->authorize('index', Consumable::class);
|
||||
|
||||
// This array is what determines which fields should be allowed to be sorted on ON the table itself, no relations
|
||||
// Relations will be handled in query scopes a little further down.
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'name',
|
||||
'order_number',
|
||||
'min_amt',
|
||||
'purchase_date',
|
||||
'purchase_cost',
|
||||
'company',
|
||||
'category',
|
||||
'model_number',
|
||||
'item_no',
|
||||
'qty',
|
||||
'image',
|
||||
'notes',
|
||||
];
|
||||
|
||||
$consumables = Consumable::select('consumables.*')
|
||||
->with('company', 'location', 'category', 'users', 'manufacturer');
|
||||
$consumables = Consumable::with('company', 'location', 'category', 'supplier', 'manufacturer')
|
||||
->withCount('users as consumables_users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$consumables = $consumables->TextSearch(e($request->input('search')));
|
||||
|
@ -89,15 +71,9 @@ class ConsumablesController extends Controller
|
|||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $consumables->count()) ? $consumables->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$allowed_columns = ['id', 'name', 'order_number', 'min_amt', 'purchase_date', 'purchase_cost', 'company', 'category', 'model_number', 'item_no', 'manufacturer', 'location', 'qty', 'image'];
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
$sort_override = $request->input('sort');
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
|
||||
|
||||
|
||||
switch ($sort_override) {
|
||||
switch ($request->input('sort')) {
|
||||
case 'category':
|
||||
$consumables = $consumables->OrderCategory($order);
|
||||
break;
|
||||
|
@ -111,10 +87,30 @@ class ConsumablesController extends Controller
|
|||
$consumables = $consumables->OrderCompany($order);
|
||||
break;
|
||||
case 'supplier':
|
||||
$components = $consumables->OrderSupplier($order);
|
||||
$consumables = $consumables->OrderSupplier($order);
|
||||
break;
|
||||
default:
|
||||
$consumables = $consumables->orderBy($column_sort, $order);
|
||||
// This array is what determines which fields should be allowed to be sorted on ON the table itself.
|
||||
// These must match a column on the consumables table directly.
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name',
|
||||
'order_number',
|
||||
'min_amt',
|
||||
'purchase_date',
|
||||
'purchase_cost',
|
||||
'company',
|
||||
'category',
|
||||
'model_number',
|
||||
'item_no',
|
||||
'manufacturer',
|
||||
'location',
|
||||
'qty',
|
||||
'image'
|
||||
];
|
||||
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$consumables = $consumables->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -131,7 +127,7 @@ class ConsumablesController extends Controller
|
|||
* @since [v4.0]
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
*/
|
||||
public function store(ImageUploadRequest $request) : JsonResponse
|
||||
public function store(StoreConsumableRequest $request) : JsonResponse
|
||||
{
|
||||
$this->authorize('create', Consumable::class);
|
||||
$consumable = new Consumable;
|
||||
|
@ -167,7 +163,7 @@ class ConsumablesController extends Controller
|
|||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $id) : JsonResponse
|
||||
public function update(StoreConsumableRequest $request, $id) : JsonResponse
|
||||
{
|
||||
$this->authorize('update', Consumable::class);
|
||||
$consumable = Consumable::findOrFail($id);
|
||||
|
|
|
@ -4,12 +4,11 @@ namespace App\Http\Controllers\Consumables;
|
|||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use \Illuminate\Http\RedirectResponse;
|
||||
|
||||
class ConsumableCheckoutController extends Controller
|
||||
{
|
||||
|
@ -20,13 +19,11 @@ class ConsumableCheckoutController extends Controller
|
|||
* @see ConsumableCheckoutController::store() method that stores the data.
|
||||
* @since [v1.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function create($id)
|
||||
public function create($id) : View | RedirectResponse
|
||||
{
|
||||
|
||||
if ($consumable = Consumable::with('users')->find($id)) {
|
||||
if ($consumable = Consumable::find($id)) {
|
||||
|
||||
$this->authorize('checkout', $consumable);
|
||||
|
||||
|
|
|
@ -8,8 +8,10 @@ use App\Http\Requests\ImageUploadRequest;
|
|||
use App\Models\Company;
|
||||
use App\Models\Consumable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use App\Http\Requests\StoreConsumableRequest;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Consumables for
|
||||
|
@ -62,7 +64,7 @@ class ConsumablesController extends Controller
|
|||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
public function store(StoreConsumableRequest $request)
|
||||
{
|
||||
$this->authorize('create', Consumable::class);
|
||||
$consumable = new Consumable();
|
||||
|
@ -99,10 +101,8 @@ class ConsumablesController extends Controller
|
|||
* @param int $consumableId
|
||||
* @see ConsumablesController::postEdit() method that stores the form data.
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function edit($consumableId = null)
|
||||
public function edit($consumableId = null) : View | RedirectResponse
|
||||
{
|
||||
if ($item = Consumable::find($consumableId)) {
|
||||
$this->authorize($item);
|
||||
|
@ -124,7 +124,7 @@ class ConsumablesController extends Controller
|
|||
* @see ConsumablesController::getEdit() method that stores the form data.
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $consumableId = null)
|
||||
public function update(StoreConsumableRequest $request, $consumableId = null)
|
||||
{
|
||||
if (is_null($consumable = Consumable::find($consumableId))) {
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
|
@ -182,6 +182,7 @@ class ConsumablesController extends Controller
|
|||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found'));
|
||||
}
|
||||
$this->authorize($consumable);
|
||||
|
||||
$consumable->delete();
|
||||
// Redirect to the locations management page
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.delete.success'));
|
||||
|
|
56
app/Http/Requests/StoreConsumableRequest.php
Normal file
56
app/Http/Requests/StoreConsumableRequest.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreConsumableRequest extends ImageUploadRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('create', new Consumable);
|
||||
}
|
||||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
|
||||
if ($this->category_id) {
|
||||
if ($category = Category::find($this->category_id)) {
|
||||
$this->merge([
|
||||
'category_type' => $category->category_type ?? null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
['category_type' => 'in:consumable'],
|
||||
parent::rules(),
|
||||
);
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
$messages = ['category_type.in' => trans('admin/consumables/message.invalid_category_type')];
|
||||
return $messages;
|
||||
}
|
||||
|
||||
public function response(array $errors)
|
||||
{
|
||||
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
|
||||
}
|
||||
}
|
|
@ -10,12 +10,21 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use App\Presenters\ConsumablePresenter;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\ConsumableAssignment;
|
||||
use App\Models\User;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\Category;
|
||||
|
||||
class Consumable extends SnipeModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $presenter = \App\Presenters\ConsumablePresenter::class;
|
||||
protected $presenter = ConsumablePresenter::class;
|
||||
use CompanyableTrait;
|
||||
use Loggable, Presentable;
|
||||
use SoftDeletes;
|
||||
|
@ -37,10 +46,10 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public $rules = [
|
||||
'name' => 'required|min:3|max:255',
|
||||
'qty' => 'required|integer|min:0',
|
||||
'qty' => 'required|integer|min:0|max:99999',
|
||||
'category_id' => 'required|integer',
|
||||
'company_id' => 'integer|nullable',
|
||||
'min_amt' => 'integer|min:0|nullable',
|
||||
'min_amt' => 'integer|min:0|max:99999|nullable',
|
||||
'purchase_cost' => 'numeric|nullable|gte:0',
|
||||
'purchase_date' => 'date_format:Y-m-d|nullable',
|
||||
];
|
||||
|
@ -109,7 +118,7 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public function uploads()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')
|
||||
return $this->hasMany(Actionlog::class, 'item_id')
|
||||
->where('item_type', '=', self::class)
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->whereNotNull('filename')
|
||||
|
@ -147,7 +156,7 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public function admin()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'user_id');
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,7 +168,7 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public function consumableAssignments()
|
||||
{
|
||||
return $this->hasMany(\App\Models\ConsumableAssignment::class);
|
||||
return $this->hasMany(ConsumableAssignment::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,7 +192,7 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public function manufacturer()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Manufacturer::class, 'manufacturer_id');
|
||||
return $this->belongsTo(Manufacturer::class, 'manufacturer_id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,7 +204,7 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public function location()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Location::class, 'location_id');
|
||||
return $this->belongsTo(Location::class, 'location_id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,7 +216,7 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Category::class, 'category_id');
|
||||
return $this->belongsTo(Category::class, 'category_id');
|
||||
}
|
||||
|
||||
|
||||
|
@ -220,7 +229,7 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public function assetlog()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')->where('item_type', self::class)->orderBy('created_at', 'desc')->withTrashed();
|
||||
return $this->hasMany(Actionlog::class, 'item_id')->where('item_type', self::class)->orderBy('created_at', 'desc')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,11 +253,10 @@ class Consumable extends SnipeModel
|
|||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function users()
|
||||
public function users() : Relation
|
||||
{
|
||||
return $this->belongsToMany(\App\Models\User::class, 'consumables_users', 'consumable_id', 'assigned_to')->withPivot('user_id')->withTrashed()->withTimestamps();
|
||||
return $this->belongsToMany(User::class, 'consumables_users', 'consumable_id', 'assigned_to')->withPivot('user_id')->withTrashed()->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -260,7 +268,7 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public function supplier()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id');
|
||||
return $this->belongsTo(Supplier::class, 'supplier_id');
|
||||
}
|
||||
|
||||
|
||||
|
@ -317,10 +325,7 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public function numCheckedOut()
|
||||
{
|
||||
$checkedout = 0;
|
||||
$checkedout = $this->users->count();
|
||||
|
||||
return $checkedout;
|
||||
return $this->consumables_users_count ?? $this->users()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -332,7 +337,7 @@ class Consumable extends SnipeModel
|
|||
*/
|
||||
public function numRemaining()
|
||||
{
|
||||
$checkedout = $this->users->count();
|
||||
$checkedout = $this->numCheckedOut();
|
||||
$total = $this->qty;
|
||||
$remaining = $total - $checkedout;
|
||||
|
||||
|
|
|
@ -3,13 +3,19 @@
|
|||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
class ConsumableAssignment extends Model
|
||||
{
|
||||
use CompanyableTrait;
|
||||
use ValidatingTrait;
|
||||
|
||||
protected $table = 'consumables_users';
|
||||
|
||||
public $rules = [
|
||||
'assigned_to' => 'required|exists:users,id',
|
||||
];
|
||||
|
||||
public function consumable()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Consumable::class);
|
||||
|
|
|
@ -5,6 +5,8 @@ namespace App\Observers;
|
|||
use App\Models\Actionlog;
|
||||
use App\Models\Consumable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ConsumableObserver
|
||||
{
|
||||
|
@ -16,13 +18,27 @@ class ConsumableObserver
|
|||
*/
|
||||
public function updated(Consumable $consumable)
|
||||
{
|
||||
|
||||
$changed = [];
|
||||
|
||||
foreach ($consumable->getRawOriginal() as $key => $value) {
|
||||
// Check and see if the value changed
|
||||
if ($consumable->getRawOriginal()[$key] != $consumable->getAttributes()[$key]) {
|
||||
$changed[$key]['old'] = $consumable->getRawOriginal()[$key];
|
||||
$changed[$key]['new'] = $consumable->getAttributes()[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if (count($changed) > 0) {
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Consumable::class;
|
||||
$logAction->item_id = $consumable->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->log_meta = json_encode($changed);
|
||||
$logAction->logaction('update');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to the Consumable created event when
|
||||
|
@ -52,6 +68,32 @@ class ConsumableObserver
|
|||
*/
|
||||
public function deleting(Consumable $consumable)
|
||||
{
|
||||
|
||||
$consumable->users()->detach();
|
||||
$uploads = $consumable->uploads;
|
||||
|
||||
foreach ($uploads as $file) {
|
||||
try {
|
||||
Storage::delete('private_uploads/consumables/'.$file->filename);
|
||||
$file->delete();
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
Storage::disk('public')->delete('consumables/'.$consumable->image);
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e);
|
||||
}
|
||||
|
||||
$consumable->image = null;
|
||||
$consumable->save();
|
||||
|
||||
|
||||
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Consumable::class;
|
||||
$logAction->item_id = $consumable->id;
|
||||
|
|
|
@ -75,7 +75,7 @@ class ActionlogPresenter extends Presenter
|
|||
}
|
||||
|
||||
if ($this->actionType()=='delete') {
|
||||
return 'fa-solid fa-user-xmark';
|
||||
return 'fa-solid fa-trash';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='update') {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
return array(
|
||||
|
||||
'invalid_category_type' => 'The category must be a consumable category.',
|
||||
'does_not_exist' => 'Consumable does not exist.',
|
||||
|
||||
'create' => array(
|
||||
|
|
|
@ -37,6 +37,25 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
<!-- total -->
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{{ trans('admin/components/general.total') }}</label>
|
||||
<div class="col-md-6">
|
||||
<p class="form-control-static">{{ $consumable->qty }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- remaining -->
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{{ trans('admin/components/general.remaining') }}</label>
|
||||
<div class="col-md-6">
|
||||
<p class="form-control-static">{{ $consumable->numRemaining() }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- User -->
|
||||
@include ('partials.forms.edit.user-select', ['translated_name' => trans('general.select_user'), 'fieldname' => 'assigned_to', 'required'=> 'true'])
|
||||
|
||||
|
|
|
@ -45,6 +45,17 @@
|
|||
</li>
|
||||
@endcan
|
||||
|
||||
<li>
|
||||
<a href="#history" data-toggle="tab">
|
||||
<span class="hidden-lg hidden-md">
|
||||
<i class="fas fa-history fa-2x" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="hidden-xs hidden-sm">
|
||||
{{ trans('general.history') }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@can('update', $consumable)
|
||||
<li class="pull-right">
|
||||
<a href="#" data-toggle="modal" data-target="#uploadFileModal">
|
||||
|
@ -95,6 +106,55 @@
|
|||
</div> <!-- close tab-pane div -->
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="history">
|
||||
<!-- checked out assets table -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table
|
||||
class="table table-striped snipe-table"
|
||||
id="consumableHistory"
|
||||
data-pagination="true"
|
||||
data-id-table="consumableHistory"
|
||||
data-search="true"
|
||||
data-side-pagination="server"
|
||||
data-show-columns="true"
|
||||
data-show-fullscreen="true"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="desc"
|
||||
data-sort-name="created_at"
|
||||
data-show-export="true"
|
||||
data-export-options='{
|
||||
"fileName": "export-consumable-{{ $consumable->id }}-history",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
}'
|
||||
|
||||
data-url="{{ route('api.activity.index', ['item_id' => $consumable->id, 'item_type' => 'consumable']) }}"
|
||||
data-cookie-id-table="assetHistory"
|
||||
data-cookie="true">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-visible="true" data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter">{{ trans('admin/hardware/table.icon') }}</th>
|
||||
<th data-visible="true" data-field="action_date" data-sortable="true" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
||||
<th data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
||||
<th data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
||||
<th data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||
<th data-visible="true" data-field="target" data-formatter="polymorphicItemFormatter">{{ trans('general.target') }}</th>
|
||||
<th data-field="note">{{ trans('general.notes') }}</th>
|
||||
<th data-field="signature_file" data-visible="false" data-formatter="imageFormatter">{{ trans('general.signature') }}</th>
|
||||
<th data-visible="false" data-field="file" data-visible="false" data-formatter="fileUploadFormatter">{{ trans('general.download') }}</th>
|
||||
<th data-field="log_meta" data-visible="true" data-formatter="changeLogFormatter">{{ trans('admin/hardware/table.changed')}}</th>
|
||||
<th data-field="remote_ip" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_ip') }}</th>
|
||||
<th data-field="user_agent" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_user_agent') }}</th>
|
||||
<th data-field="action_source" data-visible="false" data-sortable="true">{{ trans('general.action_source') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
</div> <!-- /.row -->
|
||||
</div> <!-- /.tab-pane history -->
|
||||
|
||||
|
||||
@can('consumables.files', $consumable)
|
||||
<div class="tab-pane" id="files">
|
||||
|
||||
|
@ -176,6 +236,7 @@
|
|||
<i class="fas fa-trash icon-white" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('general.delete') }}</span>
|
||||
</a>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
@ -254,6 +315,19 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
@if ($consumable->notes)
|
||||
|
||||
<div class="col-md-12">
|
||||
<strong>
|
||||
{{ trans('general.notes') }}:
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
{!! nl2br(Helper::parseEscapedMarkedownInline($consumable->notes)) !!}
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
@can('checkout', \App\Models\Consumable::class)
|
||||
|
||||
<div class="col-md-12">
|
||||
|
@ -268,21 +342,23 @@
|
|||
</button>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@can('update', \App\Models\Consumable::class)
|
||||
<div class="col-md-12">
|
||||
<a href="{{ route('consumables.edit', $consumable->id) }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print">{{ trans('button.edit') }}</a>
|
||||
</div>
|
||||
@endcan
|
||||
|
||||
@if ($consumable->notes)
|
||||
|
||||
<div class="col-md-12">
|
||||
<strong>
|
||||
{{ trans('general.notes') }}:
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
{!! nl2br(Helper::parseEscapedMarkedownInline($consumable->notes)) !!}
|
||||
</div>
|
||||
</div>
|
||||
@can('delete', $consumable)
|
||||
<div class="col-md-12" style="padding-top: 30px; padding-bottom: 30px;">
|
||||
@if ($consumable->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' => $consumable->name]) }}" data-target="#dataConfirmModal">{{ trans('general.delete') }}
|
||||
</button>
|
||||
<span class="sr-only">{{ trans('general.delete') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@endcan
|
||||
|
||||
@endcan
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -297,5 +373,16 @@
|
|||
@stop
|
||||
|
||||
@section('moar_scripts')
|
||||
<script>
|
||||
|
||||
$('#dataConfirmModal').on('show.bs.modal', function (event) {
|
||||
var content = $(event.relatedTarget).data('content');
|
||||
var title = $(event.relatedTarget).data('title');
|
||||
$(this).find(".modal-body").text(content);
|
||||
$(this).find(".modal-header").text(title);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@include ('partials.bootstrap-table', ['exportFile' => 'consumable' . $consumable->name . '-export', 'search' => false])
|
||||
@stop
|
||||
|
|
|
@ -976,7 +976,7 @@ dir="{{ in_array(app()->getLocale(),['ar-SA','fa-IR', 'he-IL']) ? 'rtl' : 'ltr'
|
|||
// Reference: https://jqueryvalidation.org/validate/
|
||||
$('#create-form').validate({
|
||||
ignore: 'input[type=hidden]',
|
||||
errorClass: 'help-block form-error',
|
||||
errorClass: 'alert-msg',
|
||||
errorElement: 'span',
|
||||
errorPlacement: function(error, element) {
|
||||
$(element).hasClass('select2') || $(element).hasClass('js-data-ajax')
|
||||
|
|
|
@ -25,4 +25,6 @@
|
|||
|
||||
|
||||
{!! $errors->first($fieldname, '<div class="col-md-8 col-md-offset-3"><span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span></div>') !!}
|
||||
|
||||
{!! $errors->first('category_type', '<div class="col-md-8 col-md-offset-3"><span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span></div>') !!}
|
||||
</div>
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
<label for="min_amt" class="col-md-3 control-label">{{ trans('general.min_amt') }}</label>
|
||||
<div class="col-md-9{{ (Helper::checkIfRequired($item, 'min_amt')) ? ' required' : '' }}">
|
||||
<div class="col-md-2" style="padding-left:0px">
|
||||
<input class="form-control col-md-3" type="text" name="min_amt" id="min_amt" aria-label="min_amt" value="{{ old('min_amt', $item->min_amt) }}" />
|
||||
<input class="form-control col-md-3" maxlength="5" type="text" name="min_amt" id="min_amt" aria-label="min_amt" value="{{ old('min_amt', $item->min_amt) }}" />
|
||||
</div>
|
||||
<div class="col-md-7" style="margin-left: -15px;">
|
||||
|
||||
<a href="#" data-tooltip="true" title="{{ trans('general.min_amt_help') }}"><i class="fas fa-info-circle" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('general.min_amt_help') }}</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
{!! $errors->first('min_amt', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
<label for="qty" class="col-md-3 control-label">{{ trans('general.quantity') }}</label>
|
||||
<div class="col-md-7{{ (Helper::checkIfRequired($item, 'qty')) ? ' required' : '' }}">
|
||||
<div class="col-md-3" style="padding-left:0px">
|
||||
<input class="form-control" type="text" name="qty" aria-label="qty" id="qty" value="{{ old('qty', $item->qty) }}" {!! (Helper::checkIfRequired($item, 'qty')) ? ' required ' : '' !!}/>
|
||||
<input class="form-control" maxlength="5" type="text" name="qty" aria-label="qty" id="qty" value="{{ old('qty', $item->qty) }}" {!! (Helper::checkIfRequired($item, 'qty')) ? ' required ' : '' !!}/>
|
||||
</div>
|
||||
<div class="col-md-12" style="padding-left:0px">
|
||||
{!! $errors->first('qty', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -151,20 +151,25 @@
|
|||
|
||||
<tr>
|
||||
<td>{{ $counter }}.{{ $assignedCounter }}</td>
|
||||
<td data-formatter="imageFormatter">
|
||||
<td>
|
||||
@if ($asset->getImageUrl())
|
||||
<img src="{{ $asset->getImageUrl() }}" class="thumbnail" style="max-height: 50px;">
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $asset->asset_tag }}</td>
|
||||
<td>{{ $asset->name }}</td>
|
||||
<td>{{ $asset->model->category->name }}</td>
|
||||
<td>{{ (($asset->model) && ($asset->model->category)) ? $asset->model->category->name : trans('general.invalid_category') }}</td>
|
||||
<td>{{ ($asset->model) ? $asset->model->name : trans('general.invalid_model') }}</td>
|
||||
<td>{{ ($asset->defaultLoc) ? $asset->defaultLoc->name : '' }}</td>
|
||||
<td>{{ ($asset->location) ? $asset->location->name : '' }}</td>
|
||||
<td>{{ $asset->model->name }}</td>
|
||||
<td>{{ $asset->serial }}</td>
|
||||
<td>{{ $asset->last_checkout }}</td>
|
||||
<td><img style="width:auto;height:100px;" src="{{ asset('/') }}display-sig/{{ $asset->assetlog->first()->accept_signature }}"></td>
|
||||
<td>
|
||||
{{ Helper::getFormattedDateObject($asset->last_checkout, 'datetime', false) }}</td>
|
||||
<td>
|
||||
@if (($asset->assetlog->first()) && ($asset->assetlog->first()->accept_signature!=''))
|
||||
<img style="width:auto;height:100px;" src="{{ asset('/') }}display-sig/{{ $asset->assetlog->first()->accept_signature }}">
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@php
|
||||
$assignedCounter++
|
||||
|
|
|
@ -32,7 +32,6 @@ class IndexCategoriesTest extends TestCase
|
|||
'limit' => '20',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'total',
|
||||
'rows',
|
||||
|
|
|
@ -54,4 +54,29 @@ class ConsumableIndexTest extends TestCase
|
|||
->assertResponseDoesNotContainInRows($consumableA)
|
||||
->assertResponseContainsInRows($consumableB);
|
||||
}
|
||||
|
||||
public function testConsumableIndexReturnsExpectedSearchResults()
|
||||
{
|
||||
Consumable::factory()->count(10)->create();
|
||||
Consumable::factory()->count(1)->create(['name' => 'My Test Consumable']);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->getJson(
|
||||
route('api.consumables.index', [
|
||||
'search' => 'My Test Consumable',
|
||||
'sort' => 'name',
|
||||
'order' => 'asc',
|
||||
'offset' => '0',
|
||||
'limit' => '20',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'total',
|
||||
'rows',
|
||||
])
|
||||
->assertJson([
|
||||
'total' => 1,
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
52
tests/Feature/Consumables/Api/ConsumableUpdateTest.php
Normal file
52
tests/Feature/Consumables/Api/ConsumableUpdateTest.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Consumables\Api;
|
||||
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Category;
|
||||
use App\Models\User;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ConsumableUpdateTest extends TestCase
|
||||
{
|
||||
|
||||
public function testCanUpdateConsumableViaPatchWithoutCategoryType()
|
||||
{
|
||||
$consumable = Consumable::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->patchJson(route('api.consumables.update', $consumable), [
|
||||
'name' => 'Test Consumable',
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('success')
|
||||
->assertStatus(200)
|
||||
->json();
|
||||
|
||||
$consumable->refresh();
|
||||
$this->assertEquals('Test Consumable', $consumable->name, 'Name was not updated');
|
||||
|
||||
}
|
||||
|
||||
public function testCannotUpdateConsumableViaPatchWithInvalidCategoryType()
|
||||
{
|
||||
$category = Category::factory()->create(['category_type' => 'asset']);
|
||||
$consumable = Consumable::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->patchJson(route('api.consumables.update', $consumable), [
|
||||
'name' => 'Test Consumable',
|
||||
'category_id' => $category->id,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error')
|
||||
->assertStatus(200)
|
||||
->json();
|
||||
|
||||
$category->refresh();
|
||||
$this->assertNotEquals('Test Consumable', $consumable->name, 'Name was not updated');
|
||||
$this->assertNotEquals('consumable', $consumable->category_id, 'Category was not updated');
|
||||
|
||||
}
|
||||
|
||||
}
|
51
tests/Feature/Consumables/Api/ConsumableViewTest.php
Normal file
51
tests/Feature/Consumables/Api/ConsumableViewTest.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Consumables\Api;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\User;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ConsumableViewTest extends TestCase
|
||||
{
|
||||
public function testConsumableViewAdheresToCompanyScoping()
|
||||
{
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
|
||||
$consumableA = Consumable::factory()->for($companyA)->create();
|
||||
$consumableB = Consumable::factory()->for($companyB)->create();
|
||||
|
||||
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||
$userInCompanyA = $companyA->users()->save(User::factory()->viewConsumables()->make());
|
||||
$userInCompanyB = $companyB->users()->save(User::factory()->viewConsumables()->make());
|
||||
|
||||
$this->settings->disableMultipleFullCompanySupport();
|
||||
|
||||
$this->actingAsForApi($superUser)
|
||||
->getJson(route('api.consumables.show', $consumableA))
|
||||
->assertOk();
|
||||
|
||||
$this->actingAsForApi($userInCompanyA)
|
||||
->getJson(route('api.consumables.show', $consumableA))
|
||||
->assertOk();
|
||||
|
||||
$this->actingAsForApi($userInCompanyB)
|
||||
->getJson(route('api.consumables.show', $consumableB))
|
||||
->assertOk();
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$this->actingAsForApi($superUser)
|
||||
->getJson(route('api.consumables.show', $consumableA))
|
||||
->assertOk();
|
||||
|
||||
$this->actingAsForApi($userInCompanyA)
|
||||
->getJson(route('api.consumables.index'))
|
||||
->assertOk();
|
||||
|
||||
$this->actingAsForApi($userInCompanyB)
|
||||
->getJson(route('api.consumables.index'))
|
||||
->assertOk();
|
||||
}
|
||||
}
|
26
tests/Feature/Consumables/Ui/ConsumableViewTest.php
Normal file
26
tests/Feature/Consumables/Ui/ConsumableViewTest.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Consumables\Ui;
|
||||
|
||||
use App\Models\Consumable;
|
||||
use App\Models\User;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ConsumableViewTest extends TestCase
|
||||
{
|
||||
public function testPermissionRequiredToViewConsumable()
|
||||
{
|
||||
$consumable = Consumable::factory()->create();
|
||||
$this->actingAs(User::factory()->create())
|
||||
->get(route('consumables.show', $consumable))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testUserCanListConsumables()
|
||||
{
|
||||
$consumable = Consumable::factory()->create();
|
||||
$this->actingAs(User::factory()->superuser()->create())
|
||||
->get(route('consumables.show', $consumable))
|
||||
->assertOk();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue