Merge branch 'develop' into snipeit_v7_laravel10

Rebuild assets and re-install from npm
This commit is contained in:
Brady Wetherington 2023-10-16 20:13:59 +01:00
commit db400dffb5
53 changed files with 4302 additions and 4287 deletions

View file

@ -2961,6 +2961,15 @@
"contributions": [
"code"
]
},
{
"login": "Singrity",
"name": "Bogdan",
"avatar_url": "https://avatars.githubusercontent.com/u/58479551?v=4",
"profile": "http://@singrity",
"contributions": [
"code"
]
}
]
}

View file

@ -1,5 +1,5 @@
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-326-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
[![All Contributors](https://img.shields.io/badge/all_contributors-327-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
## Snipe-IT - Open Source Asset Management System
@ -145,7 +145,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/28321?v=4" width="110px;"/><br /><sub>Chris Hartjes</sub>](http://www.littlehart.net/atthekeyboard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") | [<img src="https://avatars.githubusercontent.com/u/2404584?v=4" width="110px;"/><br /><sub>geo-chen</sub>](https://github.com/geo-chen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [<img src="https://avatars.githubusercontent.com/u/6006620?v=4" width="110px;"/><br /><sub>Phan Nguyen</sub>](https://github.com/nh314)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [<img src="https://avatars.githubusercontent.com/u/115993812?v=4" width="110px;"/><br /><sub>Iisakki Jaakkola</sub>](https://github.com/StarlessNights)<br />[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="110px;"/><br /><sub>Ikko Ashimine</sub>](https://bandism.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [<img src="https://avatars.githubusercontent.com/u/56871540?v=4" width="110px;"/><br /><sub>Lukas Fehling</sub>](https://github.com/lukasfehling)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [<img src="https://avatars.githubusercontent.com/u/1975990?v=4" width="110px;"/><br /><sub>Fernando Almeida</sub>](https://github.com/fernando-almeida)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") |
| [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") |
| [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") |
| [<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/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") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View file

@ -146,9 +146,8 @@ class AccessoriesFilesController extends Controller
$this->authorize('view', $accessory);
$this->authorize('accessories.files', $accessory);
if (! $log = Actionlog::find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) {
return redirect()->route('accessories.index')->with('error', trans('admin/users/message.log_record_not_found'));
}
$file = 'private_uploads/accessories/'.$log->filename;

View file

@ -346,7 +346,7 @@ class AssetsController extends Controller
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : app('api_offset_value');
$limit = app('api_limit_value');
$total = $assets->count();

View file

@ -92,7 +92,7 @@ class CategoriesController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $categories->count()) ? $categories->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $categories->count()) ? $categories->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View file

@ -56,7 +56,7 @@ class CompaniesController extends Controller
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $companies->count()) ? $companies->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $companies->count()) ? $companies->count() : app('api_offset_value');
$limit = app('api_limit_value');

View file

@ -77,7 +77,7 @@ class ComponentsController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $components->count()) ? $components->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $components->count()) ? $components->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
@ -263,7 +263,7 @@ class ComponentsController extends Controller
}
// Make sure there is at least one available to checkout
if ($component->numRemaining() <= $request->get('assigned_qty')) {
if ($component->numRemaining() < $request->get('assigned_qty')) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.checkout.unavailable', ['remaining' => $component->numRemaining(), 'requested' => $request->get('assigned_qty')])));
}

View file

@ -86,7 +86,7 @@ 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() : abs($request->input('offset'));
$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'];

View file

@ -61,7 +61,7 @@ class DepartmentsController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $departments->count()) ? $departments->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $departments->count()) ? $departments->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View file

@ -29,7 +29,7 @@ class DepreciationsController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $depreciations->count()) ? $depreciations->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $depreciations->count()) ? $depreciations->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View file

@ -36,7 +36,7 @@ class GroupsController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $groups->count()) ? $groups->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $groups->count()) ? $groups->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
@ -63,7 +63,7 @@ class GroupsController extends Controller
$group = new Group;
$group->name = $request->input('name');
$group->permissions = $request->input('permissions'); // Todo - some JSON validation stuff here
$group->permissions = json_encode($request->input('permissions')); // Todo - some JSON validation stuff here
if ($group->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.create.success')));

View file

@ -41,7 +41,7 @@ class LicenseSeatsController extends Controller
$total = $seats->count();
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $seats->count()) ? $seats->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $seats->count()) ? $seats->count() : app('api_offset_value');
if ($offset >= $total ){
$offset = 0;

View file

@ -95,7 +95,7 @@ class LicensesController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $licenses->count()) ? $licenses->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $licenses->count()) ? $licenses->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View file

@ -81,7 +81,7 @@ class LocationsController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $locations->count()) ? $locations->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $locations->count()) ? $locations->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View file

@ -62,7 +62,7 @@ class ManufacturersController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $manufacturers->count()) ? $manufacturers->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $manufacturers->count()) ? $manufacturers->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View file

@ -30,7 +30,7 @@ class PredefinedKitsController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $kits->count()) ? $kits->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $kits->count()) ? $kits->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'desc' ? 'desc' : 'asc';

View file

@ -56,7 +56,7 @@ class ReportsController extends Controller
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $actionlogs->count()) ? $actionlogs->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $actionlogs->count()) ? $actionlogs->count() : app('api_offset_value');
$limit = app('api_limit_value');
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';

View file

@ -52,7 +52,7 @@ class StatuslabelsController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $statuslabels->count()) ? $statuslabels->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $statuslabels->count()) ? $statuslabels->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View file

@ -94,7 +94,7 @@ class SuppliersController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $suppliers->count()) ? $suppliers->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $suppliers->count()) ? $suppliers->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View file

@ -192,7 +192,7 @@ class UsersController extends Controller
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $users->count()) ? $users->count() : abs($request->input('offset'));
$offset = ($request->input('offset') > $users->count()) ? $users->count() : app('api_offset_value');
$limit = app('api_limit_value');

View file

@ -86,7 +86,7 @@ class AssetFilesController extends Controller
if (isset($asset->id)) {
$this->authorize('view', $asset);
if (! $log = Actionlog::find($fileId)) {
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}

View file

@ -56,10 +56,11 @@ class ComponentCheckinController extends Controller
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function store(Request $request, $component_asset_id)
public function store(Request $request, $component_asset_id, $backto = null)
{
if ($component_assets = DB::table('components_assets')->find($component_asset_id)) {
if (is_null($component = Component::find($component_assets->component_id))) {
return redirect()->route('components.index')->with('error',
trans('admin/components/message.not_found'));
}
@ -95,6 +96,10 @@ class ComponentCheckinController extends Controller
$asset = Asset::find($component_assets->asset_id);
event(new CheckoutableCheckedIn($component, $asset, Auth::user(), $request->input('note'), Carbon::now()));
if($backto == 'asset'){
return redirect()->route('hardware.view', $asset->id)->with('success',
trans('admin/components/message.checkin.success'));
}
return redirect()->route('components.index')->with('success',
trans('admin/components/message.checkin.success'));

View file

@ -142,7 +142,7 @@ class ComponentsFilesController extends Controller
$this->authorize('view', $component);
$this->authorize('components.files', $component);
if (! $log = Actionlog::find($fileId)) {
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}

View file

@ -140,7 +140,7 @@ class ConsumablesFilesController extends Controller
$this->authorize('view', $consumable);
$this->authorize('consumables.files', $consumable);
if (! $log = Actionlog::find($fileId)) {
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}

View file

@ -137,7 +137,7 @@ class LicenseFilesController extends Controller
$this->authorize('view', $license);
$this->authorize('licenses.files', $license);
if (! $log = Actionlog::find($fileId)) {
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $license->id)->find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}

View file

@ -1043,27 +1043,34 @@ class ReportsController extends Controller
* @throws \Illuminate\Auth\Access\AuthorizationException
* @version v1.0
*/
public function sentAssetAcceptanceReminder($acceptanceId = null)
public function sentAssetAcceptanceReminder(Request $request)
{
$this->authorize('reports.view');
if (!$acceptance = CheckoutAcceptance::pending()->find($acceptanceId)) {
if (!$acceptance = CheckoutAcceptance::pending()->find($request->input('acceptance_id'))) {
\Log::debug('No pending acceptances');
// Redirect to the unaccepted assets report page with error
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
}
$assetItem = $acceptance->checkoutable;
\Log::debug(print_r($assetItem, true));
if (is_null($acceptance->created_at)){
\Log::debug('No acceptance created_at');
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
} else {
$logItem_res = $assetItem->checkouts()->where('created_at', '=', $acceptance->created_at)->get();
if ($logItem_res->isEmpty()){
\Log::debug('Acceptance date mismatch');
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
}
$logItem = $logItem_res[0];
}
if(!$assetItem->assignedTo->locale){
if (!$assetItem->assignedTo->locale){
Notification::locale(Setting::getSettings()->locale)->send(
$assetItem->assignedTo,
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)

View file

@ -136,6 +136,11 @@ class UserFilesController extends Controller
*/
public function show($userId = null, $fileId = null)
{
if (empty($fileId)) {
return redirect()->route('users.show')->with('error', 'Invalid file request');
}
$user = User::find($userId);
// the license is valid
@ -143,18 +148,20 @@ class UserFilesController extends Controller
$this->authorize('view', $user);
$log = Actionlog::find($fileId);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $user->id)->find($fileId)) {
// Display the file inline
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download('private_uploads/users/'.$log->filename, $log->filename, $headers);
// Display the file inline
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download('private_uploads/users/'.$log->filename, $log->filename, $headers);
}
return Storage::download('private_uploads/users/'.$log->filename);
}
return Storage::download('private_uploads/users/'.$log->filename);
return redirect()->route('users.index')->with('error', trans('admin/users/message.log_record_not_found'));
}
// Redirect to the user management page if the user doesn't exist

View file

@ -10,6 +10,8 @@ use App\Models\Supplier;
use App\Models\Location;
use App\Models\AssetModel;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Facades\Crypt;
class ActionlogsTransformer
{
@ -69,9 +71,36 @@ class ActionlogsTransformer
if ($custom_field->db_column == $fieldname) {
if ($custom_field->field_encrypted == '1') {
$clean_meta[$fieldname]['old'] = "************";
$clean_meta[$fieldname]['new'] = "************";
if ($custom_field->field_encrypted == '1') {
// Unset these fields. We need to decrypt them, since even if the decrypted value
// didn't change, their value in the DB will, so we have to compare the unencrypted version
// to see if the values actually did change
unset($clean_meta[$fieldname]);
unset($clean_meta[$fieldname]);
$enc_old = '';
$enc_new = '';
try {
$enc_old = \Crypt::decryptString($this->clean_field($fieldata->old));
} catch (\Exception $e) {
\Log::debug('Could not decrypt field - maybe the key changed?');
}
try {
$enc_new = \Crypt::decryptString($this->clean_field($fieldata->new));
} catch (\Exception $e) {
\Log::debug('Could not decrypt field - maybe the key changed?');
}
if ($enc_old != $enc_new) {
\Log::debug('custom fields do not match');
$clean_meta[$fieldname]['old'] = "************";
$clean_meta[$fieldname]['new'] = "************";
}
}
}
@ -178,24 +207,42 @@ class ActionlogsTransformer
if(array_key_exists('rtd_location_id',$clean_meta)) {
$clean_meta['rtd_location_id']['old'] = $clean_meta['rtd_location_id']['old'] ? "[id: ".$clean_meta['rtd_location_id']['old']."] ". $location->find($clean_meta['rtd_location_id']['old'])->name : trans('general.unassigned');
$clean_meta['rtd_location_id']['new'] = $clean_meta['rtd_location_id']['new'] ? "[id: ".$clean_meta['rtd_location_id']['new']."] ". $location->find($clean_meta['rtd_location_id']['new'])->name : trans('general.unassigned');
$oldRtd = $location->find($clean_meta['rtd_location_id']['old']);
$oldRtdName = $oldRtd ? e($oldRtd->name) : trans('general.deleted');
$newRtd = $location->find($clean_meta['rtd_location_id']['new']);
$newRtdName = $newRtd ? e($newRtd->name) : trans('general.deleted');
$clean_meta['rtd_location_id']['old'] = $clean_meta['rtd_location_id']['old'] ? "[id: ".$clean_meta['rtd_location_id']['old']."] ". $oldRtdName : '';
$clean_meta['rtd_location_id']['new'] = $clean_meta['rtd_location_id']['new'] ? "[id: ".$clean_meta['rtd_location_id']['new']."] ". $newRtdName : '';
$clean_meta['Default Location'] = $clean_meta['rtd_location_id'];
unset($clean_meta['rtd_location_id']);
}
if(array_key_exists('location_id', $clean_meta)) {
$clean_meta['location_id']['old'] = $clean_meta['location_id']['old'] ? "[id: ".$clean_meta['location_id']['old']."] ".$location->find($clean_meta['location_id']['old'])->name : trans('general.unassigned');
$clean_meta['location_id']['new'] = $clean_meta['location_id']['new'] ? "[id: ".$clean_meta['location_id']['new']."] ".$location->find($clean_meta['location_id']['new'])->name : trans('general.unassigned');
if (array_key_exists('location_id', $clean_meta)) {
$oldLocation = $location->find($clean_meta['location_id']['old']);
$oldLocationName = $oldLocation ? e($oldLocation->name) : trans('general.deleted');
$newLocation = $location->find($clean_meta['location_id']['new']);
$newLocationName = $newLocation ? e($newLocation->name) : trans('general.deleted');
$clean_meta['location_id']['old'] = $clean_meta['location_id']['old'] ? "[id: ".$clean_meta['location_id']['old']."] ". $oldLocationName : '';
$clean_meta['location_id']['new'] = $clean_meta['location_id']['new'] ? "[id: ".$clean_meta['location_id']['new']."] ". $newLocationName : '';
$clean_meta['Current Location'] = $clean_meta['location_id'];
unset($clean_meta['location_id']);
}
if(array_key_exists('model_id', $clean_meta)) {
$oldModel = $model->find($clean_meta['model_id']['old']);
$oldModelName = $oldModel->name ?? trans('admin/models/message.deleted');
$oldModelName = $oldModel ? e($oldModel->name) : trans('admin/models/message.deleted');
$newModel = $model->find($clean_meta['model_id']['new']);
$newModelName = $newModel->name ?? trans('admin/models/message.deleted');
$newModelName = $newModel ? e($newModel->name) : trans('admin/models/message.deleted');
$clean_meta['model_id']['old'] = "[id: ".$clean_meta['model_id']['old']."] ".$oldModelName;
$clean_meta['model_id']['new'] = "[id: ".$clean_meta['model_id']['new']."] ".$newModelName; /** model is required at asset creation */
@ -206,10 +253,10 @@ class ActionlogsTransformer
if(array_key_exists('company_id', $clean_meta)) {
$oldCompany = $company->find($clean_meta['company_id']['old']);
$oldCompanyName = $oldCompany->name ?? trans('admin/companies/message.deleted');
$oldCompanyName = $oldCompany ? e($oldCompany->name) : trans('admin/company/message.deleted');
$newCompany = $company->find($clean_meta['company_id']['new']);
$newCompanyName = $newCompany->name ?? trans('admin/companies/message.deleted');
$newCompanyName = $newCompany ? e($newCompany->name) : trans('admin/company/message.deleted');
$clean_meta['company_id']['old'] = $clean_meta['company_id']['old'] ? "[id: ".$clean_meta['company_id']['old']."] ". $oldCompanyName : trans('general.unassigned');
$clean_meta['company_id']['new'] = $clean_meta['company_id']['new'] ? "[id: ".$clean_meta['company_id']['new']."] ". $newCompanyName : trans('general.unassigned');
@ -219,10 +266,10 @@ class ActionlogsTransformer
if(array_key_exists('supplier_id', $clean_meta)) {
$oldSupplier = $supplier->find($clean_meta['supplier_id']['old']);
$oldSupplierName = $oldSupplier->name ?? trans('admin/suppliers/message.deleted');
$oldSupplierName = $oldSupplier ? e($oldSupplier->name) : trans('admin/suppliers/message.deleted');
$newSupplier = $supplier->find($clean_meta['supplier_id']['new']);
$newSupplierName = $newSupplier->name ?? trans('admin/suppliers/message.deleted');
$newSupplierName = $newSupplier ? e($newSupplier->name) : trans('admin/suppliers/message.deleted');
$clean_meta['supplier_id']['old'] = $clean_meta['supplier_id']['old'] ? "[id: ".$clean_meta['supplier_id']['old']."] ". $oldSupplierName : trans('general.unassigned');
$clean_meta['supplier_id']['new'] = $clean_meta['supplier_id']['new'] ? "[id: ".$clean_meta['supplier_id']['new']."] ". $newSupplierName : trans('general.unassigned');

View file

@ -18,6 +18,7 @@ use App\Notifications\CheckoutAccessoryNotification;
use App\Notifications\CheckoutAssetNotification;
use App\Notifications\CheckoutConsumableNotification;
use App\Notifications\CheckoutLicenseSeatNotification;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Support\Facades\Notification;
use Exception;
use Log;
@ -41,14 +42,9 @@ class CheckoutableListener
/**
* Make a checkout acceptance and attach it in the notification
*/
$acceptance = $this->getCheckoutAcceptance($event);
$acceptance = $this->getCheckoutAcceptance($event);
try {
if ($this->shouldSendWebhookNotification()) {
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckoutNotification($event));
}
if (! $event->checkedOutTo->locale) {
Notification::locale(Setting::getSettings()->locale)->send(
$this->getNotifiables($event),
@ -60,8 +56,15 @@ class CheckoutableListener
$this->getCheckoutNotification($event, $acceptance)
);
}
if ($this->shouldSendWebhookNotification()) {
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckoutNotification($event));
}
} catch (ClientException $e) {
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
} catch (Exception $e) {
Log::error("Exception caught during checkout notification: ".$e->getMessage());
Log::error("Exception caught during checkout notification: " . $e->getMessage());
}
}
@ -92,11 +95,6 @@ class CheckoutableListener
}
try {
if ($this->shouldSendWebhookNotification()) {
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckinNotification($event));
}
// Use default locale
if (! $event->checkedOutTo->locale) {
Notification::locale(Setting::getSettings()->locale)->send(
@ -109,8 +107,15 @@ class CheckoutableListener
$this->getCheckinNotification($event)
);
}
if ($this->shouldSendWebhookNotification()) {
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckinNotification($event));
}
} catch (ClientException $e) {
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
} catch (Exception $e) {
Log::error("Exception caught during checkin notification: ".$e->getMessage());
Log::error("Exception caught during checkin notification: " . $e->getMessage());
}
}

View file

@ -72,8 +72,7 @@ class Asset extends Depreciable
protected $casts = [
'purchase_date' => 'date',
'asset_eol_date' => 'date',
'eol_explicit' => 'boolean',
'eol_explicit' => 'boolean',
'last_checkout' => 'datetime',
'last_checkin' => 'datetime',
'expected_checkin' => 'date',

View file

@ -120,17 +120,30 @@ class AssetObserver
$logAction->user_id = Auth::id();
$logAction->logaction('delete');
}
/**
* Executes every time an asset is saved.
*
* This matters specifically because any database fields affected here MUST already exist on
* the assets table (and/or any related models), or related migrations WILL fail.
*
* For example, if there is a database migration that's a bit older and modifies an asset, if the save
* fires before a field gets created in a later migration and that field in the later migration
* is used in this observer, it doesn't actually exist yet and the migration will break unless we
* use saveQuietly() in the migration which skips this observer.
*
* @see https://github.com/snipe/snipe-it/issues/13723#issuecomment-1761315938
*/
public function saving(Asset $asset)
{
//determine if calculated eol and then calculate it - this should only happen on a new asset
if(is_null($asset->asset_eol_date) && !is_null($asset->purchase_date) && !is_null($asset->model->eol)){
// determine if calculated eol and then calculate it - this should only happen on a new asset
if (is_null($asset->asset_eol_date) && !is_null($asset->purchase_date) && !is_null($asset->model->eol)){
$asset->asset_eol_date = $asset->purchase_date->addMonths($asset->model->eol)->format('Y-m-d');
$asset->eol_explicit = false;
}
//determine if explicit and set eol_explit to true
if(!is_null($asset->asset_eol_date) && !is_null($asset->purchase_date)) {
// determine if explicit and set eol_explicit to true
if (!is_null($asset->asset_eol_date) && !is_null($asset->purchase_date)) {
if($asset->model->eol) {
$months = Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date);
if($months != $asset->model->eol) {

View file

@ -33,18 +33,33 @@ class SettingsServiceProvider extends ServiceProvider
// Make sure the limit is actually set, is an integer and does not exceed system limits
\App::singleton('api_limit_value', function () {
$limit = config('app.max_results');
$int_limit = intval(request('limit'));
if ((abs(intval(request('limit'))) > 0) && (abs(request('limit')) <= config('app.max_results'))) {
$limit = abs(request('limit'));
if ((abs($int_limit) > 0) && ($int_limit <= config('app.max_results'))) {
$limit = abs($int_limit);
}
\Log::debug('Max in env: '.config('app.max_results'));
\Log::debug('Original requested limit: '.request('limit'));
\Log::debug('Modified limit: '.$limit);
\Log::debug('------------------------------');
// \Log::debug('Max in env: '.config('app.max_results'));
// \Log::debug('Original requested limit: '.request('limit'));
// \Log::debug('Int limit: '.$int_limit);
// \Log::debug('Modified limit: '.$limit);
// \Log::debug('------------------------------');
return $limit;
});
// Make sure the offset is actually set and is an integer
\App::singleton('api_offset_value', function () {
$offset = intval(request('offset'));
// \Log::debug('Original requested offset: '.request('offset'));
// \Log::debug('Modified offset: '.$offset);
// \Log::debug('------------------------------');
return $offset;
});
/**
* Set some common variables so that they're globally available.

View file

@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v6.2.1',
'full_app_version' => 'v6.2.1 - build 11625-gc45ede2d1',
'build_version' => '11625',
'app_version' => 'v6.2.2',
'full_app_version' => 'v6.2.2 - build 11714-ga95fae0e9',
'build_version' => '11714',
'prerelease_version' => '',
'hash_version' => 'gc45ede2d1',
'full_hash' => 'v6.2.1-47-gc45ede2d1',
'hash_version' => 'ga95fae0e9',
'full_hash' => 'v6.2.2-85-ga95fae0e9',
'branch' => 'develop',
);

View file

@ -17,19 +17,30 @@ class AddEolDateOnAssetsTable extends Migration
{
Schema::table('assets', function (Blueprint $table) {
if (!Schema::hasColumn('assets', 'asset_eol_date')) {
$table->date('asset_eol_date')->after('purchase_date')->nullable()->default(null);
}
// This is a temporary shim so we don't have to modify the asset observer for migrations where
// there is a large version difference. (See the AssetObserver notes). This column gets created
// later in 2023_07_13_052204_denormalized_eol_and_add_column_for_explicit_date_to_assets.php
// but we have to temporarily create it now so the save method below doesn't break
if (!Schema::hasColumn('assets', 'eol_explicit')) {
$table->boolean('eol_explicit')->default(false)->after('asset_eol_date');
}
});
// Chunk the model query to get the models that do have an EOL date
// We use saveQuietly() here to skip the AssetObserver, since it modifies fields
// that do not yet exist on the assets table.
AssetModel::whereNotNull('eol')->chunk(10, function ($models) {
foreach ($models as $model) {
foreach ($model->assets as $asset) {
if ($asset->purchase_date!='') {
$asset->asset_eol_date = $asset->present()->eol_date();
$asset->save();
$asset->saveQuietly();
}
}

View file

@ -4,6 +4,7 @@ use App\Models\Asset;
use Carbon\CarbonImmutable;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Log;
@ -17,43 +18,40 @@ class DenormalizedEolAndAddColumnForExplicitDateToAssets extends Migration
public function up()
{
Schema::table('assets', function (Blueprint $table) {
$table->boolean('eol_explicit')->default(false)->after('asset_eol_date');
if (!Schema::hasColumn('assets', 'eol_explicit')) {
$table->boolean('eol_explicit')->default(false)->after('asset_eol_date');
}
});
// Update the eol_explicit column with the value from asset_eol_date if it exists and is different from the calculated value
Asset::whereNotNull('asset_eol_date')->with('model')->chunkById(500, function ($assetsWithEolDates) {
foreach ($assetsWithEolDates as $asset) {
if ($asset->asset_eol_date && $asset->purchase_date) {
try {
$months = CarbonImmutable::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date);
} catch (\Exception $e) {
Log::info('asset_eol_date invalid for asset '.$asset->id);
}
if ($asset->model->eol) {
if ($months != $asset->model->eol) {
Asset::whereNotNull('asset_eol_date')->with('model')->chunkById(500, function ($assetsWithEolDates) {
foreach ($assetsWithEolDates as $asset) {
if ($asset->asset_eol_date && $asset->purchase_date) {
try {
$months = CarbonImmutable::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date);
} catch (\Exception $e) {
Log::info('asset_eol_date invalid for asset ' . $asset->id);
}
if ($asset->model->eol) {
if ($months != $asset->model->eol) {
$asset->update(['eol_explicit' => true]);
}
} else {
$asset->update(['eol_explicit' => true]);
}
} else {
$asset->update(['eol_explicit' => true]);
}
}
}
});
});
// Update the asset_eol_date column with the calculated value if it doesn't exist
Asset::whereNull('asset_eol_date')->with('model')->chunkById(500, function ($assets) {
foreach ($assets as $asset) {
if ($asset->model->eol && $asset->purchase_date) {
try {
$asset_eol_date = CarbonImmutable::parse($asset->purchase_date)->addMonths($asset->model->eol)->format('Y-m-d');
$asset->update(['asset_eol_date' => $asset_eol_date]);
} catch (\Exception $e) {
Log::info('purchase date invalid for asset '.$asset->id);
}
}
}
});
DB::table('assets')
->whereNull('asset_eol_date')
->whereNotNull('purchase_date')
->whereNotNull('model_id')
->join('models', 'assets.model_id', '=', 'models.id')
->update([
'asset_eol_date' => DB::raw('DATE_ADD(purchase_date, INTERVAL ' . DB::getTablePrefix() . 'models.eol MONTH)')
]);
}

8152
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -38,7 +38,7 @@
"bootstrap-table": "1.22.1",
"chart.js": "^2.9.4",
"clipboard": "^2.0.11",
"css-loader": "^4.0.0",
"css-loader": "^5.0.0",
"ekko-lightbox": "^5.1.1",
"imagemin": "^8.0.1",
"jquery-slimscroll": "^1.3.8",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/js/dist/all.js vendored

Binary file not shown.

Binary file not shown.

View file

@ -1,8 +1,8 @@
{
"/js/build/app.js": "/js/build/app.js?id=72071a8a4dc754c61b0440d3c4119cbf",
"/js/build/app.js": "/js/build/app.js?id=4e2935e5a3efd1c7012d8495effbf2c7",
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091",
"/css/build/overrides.css": "/css/build/overrides.css?id=d96bcc45dc2a4414dd9840a14b096d4f",
"/css/build/app.css": "/css/build/app.css?id=b0aa590a3a4de33d19147264fd31b743",
"/css/build/overrides.css": "/css/build/overrides.css?id=d98b12844cffc1164b4b55914b2b4942",
"/css/build/app.css": "/css/build/app.css?id=2feeba91641a015279abd18e96be92a6",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=a67bd93bed52e6a29967fe472de66d6c",
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=268041e902b019730c23ee3875838005",
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=d409d9b1a3b69247df8b98941ba06e33",
@ -18,7 +18,7 @@
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=b48f4d8af0e1ca5621c161e93951109f",
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=f0fbbb0ac729ea092578fb05ca615460",
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
"/css/dist/all.css": "/css/dist/all.css?id=6d6bfa80b1bd2785b35a85ea81daafc8",
"/css/dist/all.css": "/css/dist/all.css?id=5da4021acc73e624ba356e6943178a76",
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=a656b2d865fe379d8851757e8e4001ef",
@ -32,8 +32,8 @@
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=2bd29fa7f9d666800c246a52ce708633",
"/js/build/vendor.js": "/js/build/vendor.js?id=ede02ee2aad89fe10c64a87f6c76838a",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=1f678160a05960c3087fb8263168ff41",
"/js/dist/all.js": "/js/dist/all.js?id=c539685e205ada5b7259499d491fae87",
"/js/dist/all-defer.js": "/js/dist/all-defer.js?id=07e52318da2cdf3171c4d88113f25fb6",
"/js/dist/all.js": "/js/dist/all.js?id=b9dbb598e7c52f20fa595893cfa1199d",
"/js/dist/all-defer.js": "/js/dist/all-defer.js?id=55eff1c09b1f966c0c4f16d898f89c86",
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=b48f4d8af0e1ca5621c161e93951109f",
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=44f9320d0739f419c9246f7f39395b02",
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=1f33ca3d860461c1127ec465ab3ebb6b",

View file

@ -680,15 +680,15 @@ th.css-accessory > .th-inner::before
margin-top:160px;
}
}
@media screen and (max-width: 771px) and (min-width: 512px){
@media screen and (max-width: 912px) and (min-width: 512px){
.sidebar-menu {
margin-top:160px
margin-top:100px
}
}
@media screen and (max-width: 1098px) and (min-width: 772px){
@media screen and (max-width: 1268px) and (min-width: 912px){
.sidebar-menu {
margin-top:98px
margin-top:50px
}
}

View file

@ -15,7 +15,7 @@ return array(
'password_resets_sent' => 'The selected users who are activated and have a valid email addresses have been sent a password reset link.',
'password_reset_sent' => 'A password reset link has been sent to :email!',
'user_has_no_email' => 'This user does not have an email address in their profile.',
'user_has_no_assets_assigned' => 'This user does not have any assets assigned',
'log_record_not_found' => 'A matching log record for this user could not be found.',
'success' => array(

View file

@ -17,7 +17,7 @@
<div class="row">
<!-- left column -->
<div class="col-md-7">
<form class="form-horizontal" method="post" action="{{ route('components.checkin.store', $component_assets->id) }}" autocomplete="off">
<form class="form-horizontal" method="post" action="{{ route('components.checkin.store', [$component_assets->id, 'backto' => 'asset']) }}" autocomplete="off">
{{csrf_field()}}
<div class="box box-default">

View file

@ -1030,6 +1030,8 @@
<th>{{ trans('general.qty') }}</th>
<th>{{ trans('general.purchase_cost') }}</th>
<th>{{trans('admin/hardware/form.serial')}}</th>
<th>{{trans('general.checkin')}}</th>
<th></th>
</thead>
<tbody>
<?php $totalCost = 0; ?>
@ -1044,6 +1046,9 @@
<td>{{ $component->pivot->assigned_qty }}</td>
<td>{{ Helper::formatCurrencyOutput($component->purchase_cost) }} each</td>
<td>{{ $component->serial }}</td>
<td>
<a href="{{ route('components.checkin.show', $component->pivot->id) }}" class="btn btn-sm bg-purple" data-tooltip="true">{{ trans('general.checkin') }}</a>
</td>
<?php $totalCost = $totalCost + ($component->purchase_cost *$component->pivot->assigned_qty) ?>
</tr>

View file

@ -77,11 +77,23 @@
<td>{!! $item['assetItem']->present()->nameUrl() !!}</td>
<td>{{ $item['assetItem']->asset_tag }}</td>
<td @if($item['acceptance']->assignedTo === null || $item['acceptance']->assignedTo->trashed()) style="text-decoration: line-through" @endif>{!! ($item['acceptance']->assignedTo) ? $item['acceptance']->assignedTo->present()->nameUrl() : trans('admin/reports/general.deleted_user') !!}</td>
<td>
<td class="white-space: nowrap;">
<nobr>
@if(!$item['acceptance']->trashed())
@if ($item['acceptance']->assignedTo)<a href="{{ route('reports/unaccepted_assets_sent_reminder', ['acceptanceId' => $item['acceptance']->id]) }}" class="btn btn-sm bg-purple" data-tooltip="true">{{ trans('admin/reports/general.send_reminder') }}</a>@endif
<form method="post" class="white-space: nowrap;" action="{{ route('reports/unaccepted_assets_sent_reminder') }}">
@if ($item['acceptance']->assignedTo)
@csrf
<input type="hidden" name="acceptance_id" value="{{ $item['acceptance']->id }}">
<button class="btn btn-sm btn-warning" data-tooltip="true" data-title="{{ trans('admin/reports/general.send_reminder') }}">
<i class="fa fa-repeat" aria-hidden="true"></i>
</button>
@endif
<a href="{{ route('reports/unaccepted_assets_delete', ['acceptanceId' => $item['acceptance']->id]) }}" class="btn btn-sm btn-danger delete-asset" data-tooltip="true" data-toggle="modal" data-content="{{ trans('general.delete_confirm', ['item' =>trans('admin/reports/general.acceptance_request')]) }}" data-title="{{ trans('general.delete') }}" onClick="return false;"><i class="fa fa-trash"></i></a>
</form>
@endif
</nobr>
</td>
</tr>
@endif

View file

@ -368,8 +368,8 @@ Route::group(['middleware' => ['auth']], function () {
'reports/unaccepted_assets/{deleted?}',
[ReportsController::class, 'getAssetAcceptanceReport']
)->name('reports/unaccepted_assets');
Route::get(
'reports/unaccepted_assets/{acceptanceId}/sent_reminder',
Route::post(
'reports/unaccepted_assets/sent_reminder',
[ReportsController::class, 'sentAssetAcceptanceReminder']
)->name('reports/unaccepted_assets_sent_reminder');
Route::delete(

View file

@ -0,0 +1,41 @@
<?php
namespace Tests\Feature\Api\Groups;
use App\Models\Group;
use App\Models\User;
use Tests\Support\InteractsWithSettings;
use Tests\TestCase;
class GroupStoreTest extends TestCase
{
use InteractsWithSettings;
public function testStoringGroupRequiresSuperAdminPermission()
{
$this->actingAsForApi(User::factory()->create())
->postJson(route('api.groups.store'))
->assertForbidden();
}
public function testCanStoreGroup()
{
$this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.groups.store'), [
'name' => 'My Awesome Group',
'permissions' => [
'admin' => '1',
'import' => '1',
'reports.view' => '0',
],
])
->assertOk();
$group = Group::where('name', 'My Awesome Group')->first();
$this->assertNotNull($group);
$this->assertEquals('1', $group->decodePermissions()['admin']);
$this->assertEquals('1', $group->decodePermissions()['import']);
$this->assertEquals('0', $group->decodePermissions()['reports.view']);
}
}