mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-31 16:37:27 -08:00
Merge branch 'refs/heads/upstream/dev' into feature/sc-26415
This commit is contained in:
commit
5cb940c2ee
|
@ -97,7 +97,7 @@ API_TOKEN_EXPIRATION_YEARS=40
|
|||
# --------------------------------------------
|
||||
# OPTIONAL: SECURITY HEADER SETTINGS
|
||||
# --------------------------------------------
|
||||
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1,172.0.0.0/8
|
||||
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1,172.16.0.0/12
|
||||
ALLOW_IFRAMING=false
|
||||
REFERRER_POLICY=same-origin
|
||||
ENABLE_CSP=false
|
||||
|
|
|
@ -84,7 +84,11 @@ Since the release of the JSON REST API, several third-party developers have been
|
|||
|
||||
### Contributing
|
||||
|
||||
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
|
||||
Please refrain from submitting issues or pull requests generated by fully-automated tools. Maintainers reserve the right, at their sole discretion, to close such submissions and to block any account responsible for them.
|
||||
|
||||
Ideally, contributions should follow from a human-to-human discussion in the form of an issue.
|
||||
|
||||
Please see the complete documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
|
||||
|
||||
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
|
||||
|
||||
|
|
|
@ -137,23 +137,24 @@ class LdapSync extends Command
|
|||
}
|
||||
|
||||
/* Determine which location to assign users to by default. */
|
||||
$location = null; // TODO - this would be better called "$default_location", which is more explicit about its purpose
|
||||
$default_location = null;
|
||||
if ($this->option('location') != '') {
|
||||
if ($location = Location::where('name', '=', $this->option('location'))->first()) {
|
||||
if ($default_location = Location::where('name', '=', $this->option('location'))->first()) {
|
||||
Log::debug('Location name ' . $this->option('location') . ' passed');
|
||||
Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')');
|
||||
Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')');
|
||||
}
|
||||
|
||||
} elseif ($this->option('location_id')) {
|
||||
//TODO - figure out how or why this is an array?
|
||||
foreach($this->option('location_id') as $location_id) {
|
||||
if ($location = Location::where('id', '=', $location_id)->first()) {
|
||||
if ($default_location = Location::where('id', '=', $location_id)->first()) {
|
||||
Log::debug('Location ID ' . $location_id . ' passed');
|
||||
Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')');
|
||||
Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (! isset($location)) {
|
||||
if (!isset($default_location)) {
|
||||
Log::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
|
||||
}
|
||||
|
||||
|
@ -229,43 +230,44 @@ class LdapSync extends Command
|
|||
|
||||
|
||||
for ($i = 0; $i < $results['count']; $i++) {
|
||||
$item = [];
|
||||
$item['username'] = $results[$i][$ldap_map["username"]][0] ?? '';
|
||||
$item['employee_number'] = $results[$i][$ldap_map["emp_num"]][0] ?? '';
|
||||
$item['lastname'] = $results[$i][$ldap_map["last_name"]][0] ?? '';
|
||||
$item['firstname'] = $results[$i][$ldap_map["first_name"]][0] ?? '';
|
||||
$item['email'] = $results[$i][$ldap_map["email"]][0] ?? '';
|
||||
$item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? '';
|
||||
$item['location_id'] = $results[$i]['location_id'] ?? '';
|
||||
$item['telephone'] = $results[$i][$ldap_map["phone"]][0] ?? '';
|
||||
$item['jobtitle'] = $results[$i][$ldap_map["jobtitle"]][0] ?? '';
|
||||
$item['country'] = $results[$i][$ldap_map["country"]][0] ?? '';
|
||||
$item['department'] = $results[$i][$ldap_map["dept"]][0] ?? '';
|
||||
$item['manager'] = $results[$i][$ldap_map["manager"]][0] ?? '';
|
||||
$item['location'] = $results[$i][$ldap_map["location"]][0] ?? '';
|
||||
$item = [];
|
||||
$item['username'] = $results[$i][$ldap_map["username"]][0] ?? '';
|
||||
$item['employee_number'] = $results[$i][$ldap_map["emp_num"]][0] ?? '';
|
||||
$item['lastname'] = $results[$i][$ldap_map["last_name"]][0] ?? '';
|
||||
$item['firstname'] = $results[$i][$ldap_map["first_name"]][0] ?? '';
|
||||
$item['email'] = $results[$i][$ldap_map["email"]][0] ?? '';
|
||||
$item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? '';
|
||||
$item['location_id'] = $results[$i]['location_id'] ?? '';
|
||||
$item['telephone'] = $results[$i][$ldap_map["phone"]][0] ?? '';
|
||||
$item['jobtitle'] = $results[$i][$ldap_map["jobtitle"]][0] ?? '';
|
||||
$item['country'] = $results[$i][$ldap_map["country"]][0] ?? '';
|
||||
$item['department'] = $results[$i][$ldap_map["dept"]][0] ?? '';
|
||||
$item['manager'] = $results[$i][$ldap_map["manager"]][0] ?? '';
|
||||
$item['location'] = $results[$i][$ldap_map["location"]][0] ?? '';
|
||||
$location = $default_location; //initially, set '$location' to the default_location (which may just be `null`)
|
||||
|
||||
// ONLY if you are using the "ldap_location" option *AND* you have an actual result
|
||||
if ($ldap_map["location"] && $item['location']) {
|
||||
$location = Location::firstOrCreate([
|
||||
'name' => $item['location'],
|
||||
]);
|
||||
}
|
||||
$department = Department::firstOrCreate([
|
||||
'name' => $item['department'],
|
||||
// ONLY if you are using the "ldap_location" option *AND* you have an actual result
|
||||
if ($ldap_map["location"] && $item['location']) {
|
||||
$location = Location::firstOrCreate([
|
||||
'name' => $item['location'],
|
||||
]);
|
||||
}
|
||||
$department = Department::firstOrCreate([
|
||||
'name' => $item['department'],
|
||||
]);
|
||||
|
||||
$user = User::where('username', $item['username'])->first();
|
||||
if ($user) {
|
||||
// Updating an existing user.
|
||||
$item['createorupdate'] = 'updated';
|
||||
} else {
|
||||
// Creating a new user.
|
||||
$user = new User;
|
||||
$user->password = $user->noPassword();
|
||||
$user->locale = app()->getLocale();
|
||||
$user->activated = 1; // newly created users can log in by default, unless AD's UAC is in use, or an active flag is set (below)
|
||||
$item['createorupdate'] = 'created';
|
||||
}
|
||||
$user = User::where('username', $item['username'])->first();
|
||||
if ($user) {
|
||||
// Updating an existing user.
|
||||
$item['createorupdate'] = 'updated';
|
||||
} else {
|
||||
// Creating a new user.
|
||||
$user = new User;
|
||||
$user->password = $user->noPassword();
|
||||
$user->locale = app()->getLocale();
|
||||
$user->activated = 1; // newly created users can log in by default, unless AD's UAC is in use, or an active flag is set (below)
|
||||
$item['createorupdate'] = 'created';
|
||||
}
|
||||
|
||||
//If a sync option is not filled in on the LDAP settings don't populate the user field
|
||||
if($ldap_map["username"] != null){
|
||||
|
@ -296,7 +298,7 @@ class LdapSync extends Command
|
|||
$user->department_id = $department->id;
|
||||
}
|
||||
if($ldap_map["location"] != null){
|
||||
$user->location_id = $location ? $location->id : null;
|
||||
$user->location_id = $location?->id;
|
||||
}
|
||||
|
||||
if($ldap_map["manager"] != null){
|
||||
|
@ -341,38 +343,38 @@ class LdapSync extends Command
|
|||
}
|
||||
}
|
||||
|
||||
// Sync activated state for Active Directory.
|
||||
if ( !empty($ldap_map["active_flag"])) { // IF we have an 'active' flag set....
|
||||
// ....then *most* things that are truthy will activate the user. Anything falsey will deactivate them.
|
||||
// (Specifically, we don't handle a value of '0.0' correctly)
|
||||
$raw_value = @$results[$i][$ldap_map["active_flag"]][0];
|
||||
$filter_var = filter_var($raw_value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
$boolean_cast = (bool)$raw_value;
|
||||
// Sync activated state for Active Directory.
|
||||
if (!empty($ldap_map["active_flag"])) { // IF we have an 'active' flag set....
|
||||
// ....then *most* things that are truthy will activate the user. Anything falsey will deactivate them.
|
||||
// (Specifically, we don't handle a value of '0.0' correctly)
|
||||
$raw_value = @$results[$i][$ldap_map["active_flag"]][0];
|
||||
$filter_var = filter_var($raw_value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
$boolean_cast = (bool) $raw_value;
|
||||
|
||||
$user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast
|
||||
$user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast
|
||||
|
||||
} elseif (array_key_exists('useraccountcontrol', $results[$i]) ) {
|
||||
// ....otherwise, (ie if no 'active' LDAP flag is defined), IF the UAC setting exists,
|
||||
// ....then use the UAC setting on the account to determine can-log-in vs. cannot-log-in
|
||||
} elseif (array_key_exists('useraccountcontrol', $results[$i])) {
|
||||
// ....otherwise, (ie if no 'active' LDAP flag is defined), IF the UAC setting exists,
|
||||
// ....then use the UAC setting on the account to determine can-log-in vs. cannot-log-in
|
||||
|
||||
|
||||
/* The following is _probably_ the correct logic, but we can't use it because
|
||||
some users may have been dependent upon the previous behavior, and this
|
||||
could cause additional access to be available to users they don't want
|
||||
to allow to log in.
|
||||
/* The following is _probably_ the correct logic, but we can't use it because
|
||||
some users may have been dependent upon the previous behavior, and this
|
||||
could cause additional access to be available to users they don't want
|
||||
to allow to log in.
|
||||
|
||||
$useraccountcontrol = $results[$i]['useraccountcontrol'][0];
|
||||
if(
|
||||
// based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties
|
||||
($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT
|
||||
!($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE
|
||||
!($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT
|
||||
) {
|
||||
$user->activated = 1;
|
||||
} else {
|
||||
$user->activated = 0;
|
||||
} */
|
||||
$enabled_accounts = [
|
||||
$useraccountcontrol = $results[$i]['useraccountcontrol'][0];
|
||||
if(
|
||||
// based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties
|
||||
($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT
|
||||
!($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE
|
||||
!($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT
|
||||
) {
|
||||
$user->activated = 1;
|
||||
} else {
|
||||
$user->activated = 0;
|
||||
} */
|
||||
$enabled_accounts = [
|
||||
'512', // 0x200 NORMAL_ACCOUNT
|
||||
'544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD
|
||||
'66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
|
||||
|
@ -385,44 +387,47 @@ class LdapSync extends Command
|
|||
'4260352', // 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH
|
||||
'1049088', // 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED
|
||||
'1114624', // 0x110200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, NOT_DELEGATED,
|
||||
];
|
||||
$user->activated = (in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts)) ? 1 : 0;
|
||||
];
|
||||
$user->activated = (in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts)) ? 1 : 0;
|
||||
|
||||
// If we're not using AD, and there isn't an activated flag set, activate all users
|
||||
} /* implied 'else' here - leave the $user->activated flag alone. Newly-created accounts will be active.
|
||||
already-existing accounts will be however the administrator has set them */
|
||||
} /* implied 'else' here - leave the $user->activated flag alone. Newly-created accounts will be active.
|
||||
already-existing accounts will be however the administrator has set them */
|
||||
|
||||
|
||||
if ($item['ldap_location_override'] == true) {
|
||||
$user->location_id = $item['location_id'];
|
||||
} elseif ((isset($location)) && (! empty($location))) {
|
||||
if ((is_array($location)) && (array_key_exists('id', $location))) {
|
||||
$user->location_id = $location['id'];
|
||||
} elseif (is_object($location)) {
|
||||
$user->location_id = $location->id;
|
||||
}
|
||||
if ($item['ldap_location_override'] == true) {
|
||||
$user->location_id = $item['location_id'];
|
||||
} elseif ((isset($location)) && (!empty($location))) {
|
||||
if ((is_array($location)) && (array_key_exists('id', $location))) {
|
||||
$user->location_id = $location['id'];
|
||||
} elseif (is_object($location)) {
|
||||
$user->location_id = $location->id; //THIS is the magic line, this should do it.
|
||||
}
|
||||
$location = null;
|
||||
$user->ldap_import = 1;
|
||||
}
|
||||
// TODO - should we be NULLING locations if $location is really `null`, and that's what we came up with?
|
||||
// will that conflict with any overriding setting that the user set? Like, if they moved someone from
|
||||
// the 'null' location to somewhere, we wouldn't want to try to override that, right?
|
||||
$location = null;
|
||||
$user->ldap_import = 1;
|
||||
|
||||
$errors = '';
|
||||
$errors = '';
|
||||
|
||||
if ($user->save()) {
|
||||
$item['note'] = $item['createorupdate'];
|
||||
$item['status'] = 'success';
|
||||
if ( $item['createorupdate'] === 'created' && $ldap_default_group) {
|
||||
$user->groups()->attach($ldap_default_group);
|
||||
}
|
||||
|
||||
} else {
|
||||
foreach ($user->getErrors()->getMessages() as $key => $err) {
|
||||
$errors .= $err[0];
|
||||
}
|
||||
$item['note'] = $errors;
|
||||
$item['status'] = 'error';
|
||||
if ($user->save()) {
|
||||
$item['note'] = $item['createorupdate'];
|
||||
$item['status'] = 'success';
|
||||
if ($item['createorupdate'] === 'created' && $ldap_default_group) {
|
||||
$user->groups()->attach($ldap_default_group);
|
||||
}
|
||||
|
||||
array_push($summary, $item);
|
||||
} else {
|
||||
foreach ($user->getErrors()->getMessages() as $key => $err) {
|
||||
$errors .= $err[0];
|
||||
}
|
||||
$item['note'] = $errors;
|
||||
$item['status'] = 'error';
|
||||
}
|
||||
|
||||
array_push($summary, $item);
|
||||
}
|
||||
|
||||
if ($this->option('summary')) {
|
||||
|
|
|
@ -137,7 +137,6 @@ class AccessoriesController extends Controller
|
|||
*/
|
||||
public function store(StoreAccessoryRequest $request)
|
||||
{
|
||||
$this->authorize('create', Accessory::class);
|
||||
$accessory = new Accessory;
|
||||
$accessory->fill($request->all());
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
@ -197,9 +196,6 @@ class AccessoriesController extends Controller
|
|||
$this->authorize('view', Accessory::class);
|
||||
|
||||
$accessory = Accessory::with('lastCheckout')->findOrFail($id);
|
||||
if (! Company::isCurrentUserHasAccess($accessory)) {
|
||||
return ['total' => 0, 'rows' => []];
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
@ -325,7 +321,7 @@ class AccessoriesController extends Controller
|
|||
$accessory = Accessory::find($accessory_checkout->accessory_id);
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
$logaction = $accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
|
||||
$accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
|
||||
|
||||
// Was the accessory updated?
|
||||
if ($accessory_checkout->delete()) {
|
||||
|
@ -333,14 +329,6 @@ class AccessoriesController extends Controller
|
|||
$user = User::find($accessory_checkout->assigned_to);
|
||||
}
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
$data['first_name'] = $user->first_name;
|
||||
$data['last_name'] = $user->last_name;
|
||||
$data['item_name'] = $accessory->name;
|
||||
$data['checkin_date'] = $logaction->created_at;
|
||||
$data['item_tag'] = '';
|
||||
$data['note'] = $logaction->note;
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkin.success')));
|
||||
}
|
||||
|
||||
|
|
|
@ -395,7 +395,7 @@ class AssetsController extends Controller
|
|||
|
||||
// This may not work for all databases, but it works for MySQL
|
||||
if ($numeric_sort) {
|
||||
$assets->orderByRaw($sort_override . ' * 1 ' . $order);
|
||||
$assets->orderByRaw(DB::getTablePrefix() . 'assets.' . $sort_override . ' * 1 ' . $order);
|
||||
} else {
|
||||
$assets->orderBy($sort_override, $order);
|
||||
}
|
||||
|
|
|
@ -42,13 +42,14 @@ class UsersController extends Controller
|
|||
|
||||
$users = User::select([
|
||||
'users.activated',
|
||||
'users.created_by',
|
||||
'users.address',
|
||||
'users.avatar',
|
||||
'users.city',
|
||||
'users.company_id',
|
||||
'users.country',
|
||||
'users.created_by',
|
||||
'users.created_at',
|
||||
'users.updated_at',
|
||||
'users.deleted_at',
|
||||
'users.department_id',
|
||||
'users.email',
|
||||
|
@ -67,7 +68,6 @@ class UsersController extends Controller
|
|||
'users.state',
|
||||
'users.two_factor_enrolled',
|
||||
'users.two_factor_optin',
|
||||
'users.updated_at',
|
||||
'users.username',
|
||||
'users.zip',
|
||||
'users.remote',
|
||||
|
@ -255,6 +255,7 @@ class UsersController extends Controller
|
|||
'groups',
|
||||
'activated',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'two_factor_enrolled',
|
||||
'two_factor_optin',
|
||||
'last_login',
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* This controller provide the health route for
|
||||
|
@ -15,13 +16,35 @@ use Illuminate\Routing\Controller as BaseController;
|
|||
*/
|
||||
class HealthController extends BaseController
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('health');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a fixed JSON content ({ "status": "ok"}) which indicate the app is up and running
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
]);
|
||||
try {
|
||||
|
||||
if (DB::select('select 2 + 2')) {
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
]);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Could not connect to database');
|
||||
return response()->json([
|
||||
'status' => 'database connection failed',
|
||||
], 500);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,14 +194,14 @@ class ProfileController extends Controller
|
|||
*/
|
||||
public function printInventory() : View
|
||||
{
|
||||
$show_user = auth()->user();
|
||||
$show_users = User::where('id',auth()->user()->id)->get();
|
||||
|
||||
return view('users/print')
|
||||
->with('assets', auth()->user()->assets)
|
||||
->with('licenses', $show_user->licenses()->get())
|
||||
->with('accessories', $show_user->accessories()->get())
|
||||
->with('consumables', $show_user->consumables()->get())
|
||||
->with('show_user', $show_user)
|
||||
->with('assets', auth()->user()->assets())
|
||||
->with('licenses', auth()->user()->licenses()->get())
|
||||
->with('accessories', auth()->user()->accessories()->get())
|
||||
->with('consumables', auth()->user()->consumables()->get())
|
||||
->with('users', $show_users)
|
||||
->with('settings', Setting::getSettings());
|
||||
}
|
||||
|
||||
|
@ -222,7 +222,12 @@ class ProfileController extends Controller
|
|||
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
|
||||
}
|
||||
|
||||
$user->notify((new CurrentInventory($user)));
|
||||
try {
|
||||
$user->notify((new CurrentInventory($user)));
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,11 @@ use App\Helpers\StorageHelper;
|
|||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Requests\SettingsSamlRequest;
|
||||
use App\Http\Requests\SetupUserRequest;
|
||||
use App\Http\Requests\StoreLdapSettings;
|
||||
use App\Http\Requests\StoreLocalizationSettings;
|
||||
use App\Http\Requests\StoreNotificationSettings;
|
||||
use App\Http\Requests\StoreLabelSettings;
|
||||
use App\Http\Requests\StoreSecuritySettings;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Group;
|
||||
use App\Models\Setting;
|
||||
|
@ -273,20 +278,6 @@ class SettingsController extends Controller
|
|||
return view('settings/index', compact('settings'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the admin settings page.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function getEdit() : View
|
||||
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
return view('settings/general', compact('setting'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a form to allow a super admin to update settings.
|
||||
|
@ -488,7 +479,7 @@ class SettingsController extends Controller
|
|||
*
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function postSecurity(Request $request) : RedirectResponse
|
||||
public function postSecurity(StoreSecuritySettings $request) : RedirectResponse
|
||||
{
|
||||
$this->validate($request, [
|
||||
'pwd_secure_complexity' => 'array',
|
||||
|
@ -558,7 +549,7 @@ class SettingsController extends Controller
|
|||
*
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function postLocalization(Request $request) : RedirectResponse
|
||||
public function postLocalization(StoreLocalizationSettings $request) : RedirectResponse
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
|
@ -601,7 +592,7 @@ class SettingsController extends Controller
|
|||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function postAlerts(Request $request) : RedirectResponse
|
||||
public function postAlerts(StoreNotificationSettings $request) : RedirectResponse
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
|
@ -782,7 +773,7 @@ class SettingsController extends Controller
|
|||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function postLabels(Request $request) : RedirectResponse
|
||||
public function postLabels(StoreLabelSettings $request) : RedirectResponse
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
|
@ -861,26 +852,7 @@ class SettingsController extends Controller
|
|||
{
|
||||
$setting = Setting::getSettings();
|
||||
$groups = Group::pluck('name', 'id');
|
||||
|
||||
|
||||
/**
|
||||
* This validator is only temporary (famous last words.) - @snipe
|
||||
*/
|
||||
$messages = [
|
||||
'ldap_username_field.not_in' => '<code>sAMAccountName</code> (mixed case) will likely not work. You should use <code>samaccountname</code> (lowercase) instead. ',
|
||||
'ldap_auth_filter_query.not_in' => '<code>uid=samaccountname</code> is probably not a valid auth filter. You probably want <code>uid=</code> ',
|
||||
'ldap_filter.regex' => 'This value should probably not be wrapped in parentheses.',
|
||||
];
|
||||
|
||||
$validator = Validator::make($setting->toArray(), [
|
||||
'ldap_username_field' => 'not_in:sAMAccountName',
|
||||
'ldap_auth_filter_query' => 'not_in:uid=samaccountname|required_if:ldap_enabled,1',
|
||||
'ldap_filter' => 'nullable|regex:"^[^(]"|required_if:ldap_enabled,1',
|
||||
], $messages);
|
||||
|
||||
|
||||
|
||||
return view('settings.ldap', compact('setting', 'groups'))->withErrors($validator);
|
||||
return view('settings.ldap', compact('setting', 'groups'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -889,7 +861,7 @@ class SettingsController extends Controller
|
|||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function postLdapSettings(Request $request) : RedirectResponse
|
||||
public function postLdapSettings(StoreLdapSettings $request) : RedirectResponse
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
|
|
|
@ -53,6 +53,10 @@ class Kernel extends HttpKernel
|
|||
\App\Http\Middleware\CheckLocale::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'health' => [
|
||||
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -69,5 +73,6 @@ class Kernel extends HttpKernel
|
|||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'health' => null,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -7,14 +7,19 @@ use Closure;
|
|||
|
||||
class CheckForSetup
|
||||
{
|
||||
|
||||
protected $except = [
|
||||
'_debugbar*',
|
||||
'health'
|
||||
];
|
||||
|
||||
public function handle($request, Closure $next, $guard = null)
|
||||
{
|
||||
|
||||
/**
|
||||
* This is dumb
|
||||
* @todo Check on removing this, not sure if it's still needed
|
||||
* Skip this middleware for the debugbar and health check
|
||||
*/
|
||||
if ($request->is('_debugbar*')) {
|
||||
if ($request->is($this->except)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
@ -25,7 +30,7 @@ class CheckForSetup
|
|||
return $next($request);
|
||||
}
|
||||
} else {
|
||||
if (! ($request->is('setup*')) && ! ($request->is('.env')) && ! ($request->is('health'))) {
|
||||
if (! ($request->is('setup*')) && ! ($request->is('.env'))) {
|
||||
return redirect(config('app.url').'/setup');
|
||||
}
|
||||
|
||||
|
|
|
@ -26,18 +26,11 @@ class StoreAssetRequest extends ImageUploadRequest
|
|||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
// Guard against users passing in an array for company_id instead of an integer.
|
||||
// If the company_id is not an integer then we simply use what was
|
||||
// provided to be caught by model level validation later.
|
||||
$idForCurrentUser = is_int($this->company_id)
|
||||
? Company::getIdForCurrentUser($this->company_id)
|
||||
: $this->company_id;
|
||||
|
||||
$this->parseLastAuditDate();
|
||||
|
||||
$this->merge([
|
||||
'asset_tag' => $this->asset_tag ?? Asset::autoincrement_asset(),
|
||||
'company_id' => $idForCurrentUser,
|
||||
'company_id' => Company::getIdForCurrentUser($this->company_id),
|
||||
'assigned_to' => $assigned_to ?? null,
|
||||
]);
|
||||
}
|
||||
|
|
41
app/Http/Requests/StoreLabelSettings.php
Normal file
41
app/Http/Requests/StoreLabelSettings.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreLabelSettings extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('superuser');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'labels_per_page' => 'numeric',
|
||||
'labels_width' => 'numeric',
|
||||
'labels_height' => 'numeric',
|
||||
'labels_pmargin_left' => 'numeric|nullable',
|
||||
'labels_pmargin_right' => 'numeric|nullable',
|
||||
'labels_pmargin_top' => 'numeric|nullable',
|
||||
'labels_pmargin_bottom' => 'numeric|nullable',
|
||||
'labels_display_bgutter' => 'numeric|nullable',
|
||||
'labels_display_sgutter' => 'numeric|nullable',
|
||||
'labels_fontsize' => 'numeric|min:5',
|
||||
'labels_pagewidth' => 'numeric|nullable',
|
||||
'labels_pageheight' => 'numeric|nullable',
|
||||
'qr_text' => 'max:31|nullable',
|
||||
];
|
||||
}
|
||||
}
|
38
app/Http/Requests/StoreLdapSettings.php
Normal file
38
app/Http/Requests/StoreLdapSettings.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreLdapSettings extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('superuser');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'ldap_username_field' => 'not_in:sAMAccountName|required_if:ldap_enabled,1',
|
||||
'ldap_auth_filter_query' => 'not_in:uid=samaccountname|required_if:ldap_enabled,1',
|
||||
'ldap_filter' => 'nullable|regex:"^[^(]"|required_if:ldap_enabled,1',
|
||||
'ldap_server' => 'nullable|required_if:ldap_enabled,1|starts_with:ldap://,ldaps://',
|
||||
'ldap_uname' => 'nullable|required_if:ldap_enabled,1',
|
||||
'ldap_pword' => 'nullable|required_if:ldap_enabled,1',
|
||||
'ldap_basedn' => 'nullable|required_if:ldap_enabled,1',
|
||||
'ldap_fname_field' => 'nullable|required_if:ldap_enabled,1',
|
||||
'custom_forgot_pass_url' => 'nullable|url',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
30
app/Http/Requests/StoreLocalizationSettings.php
Normal file
30
app/Http/Requests/StoreLocalizationSettings.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreLocalizationSettings extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('superuser');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'default_currency' => 'required',
|
||||
'locale' => 'required',
|
||||
];
|
||||
}
|
||||
}
|
37
app/Http/Requests/StoreNotificationSettings.php
Normal file
37
app/Http/Requests/StoreNotificationSettings.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreNotificationSettings extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('superuser');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'alert_email' => 'email_array|nullable',
|
||||
'admin_cc_email' => 'email|nullable',
|
||||
'alert_threshold' => 'numeric|nullable|gt:0',
|
||||
'alert_interval' => 'numeric|nullable|gt:0',
|
||||
'audit_warning_days' => 'numeric|nullable|gt:0',
|
||||
'due_checkin_days' => 'numeric|nullable|gt:0',
|
||||
'audit_interval' => 'numeric|nullable|gt:0',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
35
app/Http/Requests/StoreSecuritySettings.php
Normal file
35
app/Http/Requests/StoreSecuritySettings.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreSecuritySettings extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('superuser');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'pwd_secure_min' => 'numeric|required|min:8',
|
||||
'custom_forgot_pass_url' => 'url|nullable',
|
||||
'privacy_policy_link' => 'nullable|url',
|
||||
'login_remote_user_enabled' => 'numeric|nullable',
|
||||
'login_common_disabled' => 'numeric|nullable',
|
||||
'login_remote_user_custom_logout_url' => 'string|nullable',
|
||||
'login_remote_user_header_name' => 'string|nullable',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -66,7 +66,7 @@ class AssetMaintenancesTransformer
|
|||
'completion_date' => Helper::getFormattedDateObject($assetmaintenance->completion_date, 'date'),
|
||||
'user_id' => ($assetmaintenance->adminuser) ? [
|
||||
'id' => $assetmaintenance->adminuser->id,
|
||||
'name'=> e($assetmaintenance->admin->getFullNameAttribute())
|
||||
'name'=> e($assetmaintenance->adminuser->present()->fullName())
|
||||
] : null, // legacy to not change the shape of the API
|
||||
'created_by' => ($assetmaintenance->adminuser) ? [
|
||||
'id' => (int) $assetmaintenance->adminuser->id,
|
||||
|
|
|
@ -164,6 +164,7 @@ abstract class Importer
|
|||
|
||||
$this->log('------------- Action Summary ----------------');
|
||||
}
|
||||
Model::reguard();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -43,16 +43,16 @@ class Asset extends Depreciable
|
|||
|
||||
/**
|
||||
* Run after the checkout acceptance was declined by the user
|
||||
*
|
||||
*
|
||||
* @param User $acceptedBy
|
||||
* @param string $signature
|
||||
*/
|
||||
*/
|
||||
public function declinedCheckout(User $declinedBy, $signature)
|
||||
{
|
||||
$this->assigned_to = null;
|
||||
$this->assigned_type = null;
|
||||
$this->accepted = null;
|
||||
$this->save();
|
||||
$this->accepted = null;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -368,7 +368,7 @@ class Asset extends Depreciable
|
|||
if ($this->save()) {
|
||||
if (is_int($admin)) {
|
||||
$checkedOutBy = User::findOrFail($admin);
|
||||
} elseif (get_class($admin) === \App\Models\User::class) {
|
||||
} elseif ($admin && get_class($admin) === \App\Models\User::class) {
|
||||
$checkedOutBy = $admin;
|
||||
} else {
|
||||
$checkedOutBy = auth()->user();
|
||||
|
@ -1705,7 +1705,7 @@ class Asset extends Depreciable
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* THIS CLUNKY BIT IS VERY IMPORTANT
|
||||
|
@ -1726,7 +1726,7 @@ class Asset extends Depreciable
|
|||
* assets.location would fail, as that field doesn't exist -- plus we're already searching
|
||||
* against those relationships earlier in this method.
|
||||
*
|
||||
* - snipe
|
||||
* - snipe
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
|||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'user_id')
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,13 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Import extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'header_row' => 'array',
|
||||
'first_row' => 'array',
|
||||
|
|
|
@ -42,7 +42,7 @@ class Location extends SnipeModel
|
|||
];
|
||||
|
||||
/**
|
||||
* Whether the model should inject it's identifier to the unique
|
||||
* Whether the model should inject its identifier to the unique
|
||||
* validation rules before attempting validation. If this property
|
||||
* is not set in the model it will default to true.
|
||||
*
|
||||
|
|
|
@ -117,7 +117,6 @@ trait Loggable
|
|||
*/
|
||||
public function logCheckin($target, $note, $action_date = null, $originalValues = [])
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$log = new Actionlog;
|
||||
|
||||
if($target != null){
|
||||
|
@ -171,39 +170,6 @@ trait Loggable
|
|||
|
||||
$log->logaction('checkin from');
|
||||
|
||||
// $params = [
|
||||
// 'target' => $target,
|
||||
// 'item' => $log->item,
|
||||
// 'admin' => $log->user,
|
||||
// 'note' => $note,
|
||||
// 'target_type' => $log->target_type,
|
||||
// 'settings' => $settings,
|
||||
// ];
|
||||
//
|
||||
//
|
||||
// $checkinClass = null;
|
||||
//
|
||||
// if (method_exists($target, 'notify')) {
|
||||
// try {
|
||||
// $target->notify(new static::$checkinClass($params));
|
||||
// } catch (\Exception $e) {
|
||||
// Log::debug($e);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// // Send to the admin, if settings dictate
|
||||
// $recipient = new \App\Models\Recipients\AdminRecipient();
|
||||
//
|
||||
// if (($settings->admin_cc_email!='') && (static::$checkinClass!='')) {
|
||||
// try {
|
||||
// $recipient->notify(new static::$checkinClass($params));
|
||||
// } catch (\Exception $e) {
|
||||
// Log::debug($e);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,36 +51,7 @@ class Setting extends Model
|
|||
*/
|
||||
protected $rules = [
|
||||
'brand' => 'required|min:1|numeric',
|
||||
'qr_text' => 'max:31|nullable',
|
||||
'alert_email' => 'email_array|nullable',
|
||||
'admin_cc_email' => 'email|nullable',
|
||||
'default_currency' => 'required',
|
||||
'locale' => 'required',
|
||||
'labels_per_page' => 'numeric',
|
||||
'labels_width' => 'numeric',
|
||||
'labels_height' => 'numeric',
|
||||
'labels_pmargin_left' => 'numeric|nullable',
|
||||
'labels_pmargin_right' => 'numeric|nullable',
|
||||
'labels_pmargin_top' => 'numeric|nullable',
|
||||
'labels_pmargin_bottom' => 'numeric|nullable',
|
||||
'labels_display_bgutter' => 'numeric|nullable',
|
||||
'labels_display_sgutter' => 'numeric|nullable',
|
||||
'labels_fontsize' => 'numeric|min:5',
|
||||
'labels_pagewidth' => 'numeric|nullable',
|
||||
'labels_pageheight' => 'numeric|nullable',
|
||||
'login_remote_user_enabled' => 'numeric|nullable',
|
||||
'login_common_disabled' => 'numeric|nullable',
|
||||
'login_remote_user_custom_logout_url' => 'string|nullable',
|
||||
'login_remote_user_header_name' => 'string|nullable',
|
||||
'thumbnail_max_h' => 'numeric|max:500|min:25',
|
||||
'pwd_secure_min' => 'numeric|required|min:8',
|
||||
'alert_threshold' => 'numeric|nullable',
|
||||
'alert_interval' => 'numeric|nullable',
|
||||
'audit_warning_days' => 'numeric|nullable',
|
||||
'due_checkin_days' => 'numeric|nullable',
|
||||
'audit_interval' => 'numeric|nullable',
|
||||
'custom_forgot_pass_url' => 'url|nullable',
|
||||
'privacy_policy_link' => 'nullable|url',
|
||||
'google_client_id' => 'nullable|ends_with:apps.googleusercontent.com'
|
||||
];
|
||||
|
||||
|
|
|
@ -116,12 +116,6 @@ class AssetMaintenancesPresenter extends Presenter
|
|||
'sortable' => true,
|
||||
'title' => trans('admin/asset_maintenances/form.cost'),
|
||||
'class' => 'text-right',
|
||||
], [
|
||||
'field' => 'user_id',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.admin'),
|
||||
'formatter' => 'usersLinkObjFormatter',
|
||||
], [
|
||||
'field' => 'created_by',
|
||||
'searchable' => false,
|
||||
|
|
|
@ -31,6 +31,7 @@ class ValidationServiceProvider extends ServiceProvider
|
|||
Validator::extend('email_array', function ($attribute, $value, $parameters, $validator) {
|
||||
$value = str_replace(' ', '', $value);
|
||||
$array = explode(',', $value);
|
||||
$email_to_validate = [];
|
||||
|
||||
foreach ($array as $email) { //loop over values
|
||||
$email_to_validate['alert_email'][] = $email;
|
||||
|
@ -38,7 +39,7 @@ class ValidationServiceProvider extends ServiceProvider
|
|||
|
||||
$rules = ['alert_email.*'=>'email'];
|
||||
$messages = [
|
||||
'alert_email.*'=>trans('validation.email_array'),
|
||||
'alert_email.*' => trans('validation.custom.email_array'),
|
||||
];
|
||||
|
||||
$validator = Validator::make($email_to_validate, $rules, $messages);
|
||||
|
|
1297
composer.lock
generated
1297
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -3,7 +3,6 @@
|
|||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Category;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
|
@ -156,4 +155,19 @@ class AccessoryFactory extends Factory
|
|||
]);
|
||||
});
|
||||
}
|
||||
|
||||
public function checkedOutToUsers(array $users)
|
||||
{
|
||||
return $this->afterCreating(function (Accessory $accessory) use ($users) {
|
||||
foreach ($users as $user) {
|
||||
$accessory->checkouts()->create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => 1,
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_type' => User::class,
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
146
database/factories/ImportFactory.php
Normal file
146
database/factories/ImportFactory.php
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Import;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Tests\Support\Importing;
|
||||
|
||||
/**
|
||||
* @extends Factory<Import>
|
||||
*/
|
||||
class ImportFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected $model = Import::class;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->company,
|
||||
'file_path' => Str::random().'.csv',
|
||||
'filesize' => $this->faker->randomDigitNotNull(),
|
||||
'field_map' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an accessory import type.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function accessory()
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
$fileBuilder = Importing\AccessoriesImportFileBuilder::new();
|
||||
|
||||
$attributes['name'] = "{$attributes['name']} Accessories";
|
||||
$attributes['import_type'] = 'accessory';
|
||||
$attributes['header_row'] = $fileBuilder->toCsv()[0];
|
||||
$attributes['first_row'] = $fileBuilder->firstRow();
|
||||
|
||||
return $attributes;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an asset import type.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function asset()
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
$fileBuilder = Importing\AssetsImportFileBuilder::new();
|
||||
|
||||
$attributes['name'] = "{$attributes['name']} Assets";
|
||||
$attributes['import_type'] = 'asset';
|
||||
$attributes['header_row'] = $fileBuilder->toCsv()[0];
|
||||
$attributes['first_row'] = $fileBuilder->firstRow();
|
||||
|
||||
return $attributes;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a component import type.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function component()
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
$fileBuilder = Importing\ComponentsImportFileBuilder::new();
|
||||
|
||||
$attributes['name'] = "{$attributes['name']} Components";
|
||||
$attributes['import_type'] = 'component';
|
||||
$attributes['header_row'] = $fileBuilder->toCsv()[0];
|
||||
$attributes['first_row'] = $fileBuilder->firstRow();
|
||||
|
||||
return $attributes;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a consumable import type.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function consumable()
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
$fileBuilder = Importing\ConsumablesImportFileBuilder::new();
|
||||
|
||||
$attributes['name'] = "{$attributes['name']} Consumables";
|
||||
$attributes['import_type'] = 'consumable';
|
||||
$attributes['header_row'] = $fileBuilder->toCsv()[0];
|
||||
$attributes['first_row'] = $fileBuilder->firstRow();
|
||||
|
||||
return $attributes;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a license import type.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function license()
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
$fileBuilder = Importing\LicensesImportFileBuilder::new();
|
||||
|
||||
$attributes['name'] = "{$attributes['name']} Licenses";
|
||||
$attributes['import_type'] = 'license';
|
||||
$attributes['header_row'] = $fileBuilder->toCsv()[0];
|
||||
$attributes['first_row'] = $fileBuilder->firstRow();
|
||||
|
||||
return $attributes;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a users import type.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
$fileBuilder = Importing\UsersImportFileBuilder::new();
|
||||
|
||||
$attributes['name'] = "{$attributes['name']} Employees";
|
||||
$attributes['import_type'] = 'user';
|
||||
$attributes['header_row'] = $fileBuilder->toCsv()[0];
|
||||
$attributes['first_row'] = $fileBuilder->firstRow();
|
||||
|
||||
return $attributes;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -7,6 +7,9 @@ use App\Models\User;
|
|||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use \Auth;
|
||||
|
||||
/**
|
||||
* @extends Factory<User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
|
|
Binary file not shown.
Binary file not shown.
BIN
public/css/dist/all.css
vendored
BIN
public/css/dist/all.css
vendored
Binary file not shown.
|
@ -2,8 +2,8 @@
|
|||
"/js/build/app.js": "/js/build/app.js?id=5e9ac5c1a7e089f056fb1dba566193a6",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=f0b08873a06bb54daeee176a9459f4a9",
|
||||
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=f4397c717b99fce41a633ca6edd5d1f4",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=efd9f439cb0586512d03172bcd9a5752",
|
||||
"/css/build/app.css": "/css/build/app.css?id=2f45befb40b9d7f038eeae9569c33a5f",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=1c3ffc5fb379e21523f2a9b03f986edb",
|
||||
"/css/build/app.css": "/css/build/app.css?id=d04f32982fb319ac35a32d362089f18b",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=4ea0068716c1bb2434d87a16d51b98c9",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=393aaa7b368b0670fc42434c8cca7dc7",
|
||||
|
@ -19,7 +19,7 @@
|
|||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=0640e45bad692dcf62873c6e85904899",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=e9509d7591637153f667461642e47e30",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=9f69886d7a8e4c383cd09a48573922b7",
|
||||
"/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",
|
||||
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
|
||||
|
|
242
public/vendor/livewire/livewire.esm.js
vendored
242
public/vendor/livewire/livewire.esm.js
vendored
|
@ -1432,10 +1432,10 @@ var require_module_cjs = __commonJS({
|
|||
});
|
||||
}
|
||||
function cleanupElement(el) {
|
||||
if (el._x_cleanups) {
|
||||
while (el._x_cleanups.length)
|
||||
el._x_cleanups.pop()();
|
||||
}
|
||||
var _a, _b;
|
||||
(_a = el._x_effects) == null ? void 0 : _a.forEach(dequeueJob);
|
||||
while ((_b = el._x_cleanups) == null ? void 0 : _b.length)
|
||||
el._x_cleanups.pop()();
|
||||
}
|
||||
var observer = new MutationObserver(onMutate);
|
||||
var currentlyObserving = false;
|
||||
|
@ -1673,27 +1673,23 @@ var require_module_cjs = __commonJS({
|
|||
magics[name] = callback;
|
||||
}
|
||||
function injectMagics(obj, el) {
|
||||
let memoizedUtilities = getUtilities(el);
|
||||
Object.entries(magics).forEach(([name, callback]) => {
|
||||
let memoizedUtilities = null;
|
||||
function getUtilities() {
|
||||
if (memoizedUtilities) {
|
||||
return memoizedUtilities;
|
||||
} else {
|
||||
let [utilities, cleanup] = getElementBoundUtilities(el);
|
||||
memoizedUtilities = { interceptor, ...utilities };
|
||||
onElRemoved(el, cleanup);
|
||||
return memoizedUtilities;
|
||||
}
|
||||
}
|
||||
Object.defineProperty(obj, `$${name}`, {
|
||||
get() {
|
||||
return callback(el, getUtilities());
|
||||
return callback(el, memoizedUtilities);
|
||||
},
|
||||
enumerable: false
|
||||
});
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
function getUtilities(el) {
|
||||
let [utilities, cleanup] = getElementBoundUtilities(el);
|
||||
let utils = { interceptor, ...utilities };
|
||||
onElRemoved(el, cleanup);
|
||||
return utils;
|
||||
}
|
||||
function tryCatch(el, expression, callback, ...args) {
|
||||
try {
|
||||
return callback(...args);
|
||||
|
@ -2067,8 +2063,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
function destroyTree(root, walker = walk) {
|
||||
walker(root, (el) => {
|
||||
cleanupAttributes(el);
|
||||
cleanupElement(el);
|
||||
cleanupAttributes(el);
|
||||
});
|
||||
}
|
||||
function warnAboutMissingPlugins() {
|
||||
|
@ -2648,34 +2644,37 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
return rawValue ? Boolean(rawValue) : null;
|
||||
}
|
||||
var booleanAttributes = /* @__PURE__ */ new Set([
|
||||
"allowfullscreen",
|
||||
"async",
|
||||
"autofocus",
|
||||
"autoplay",
|
||||
"checked",
|
||||
"controls",
|
||||
"default",
|
||||
"defer",
|
||||
"disabled",
|
||||
"formnovalidate",
|
||||
"inert",
|
||||
"ismap",
|
||||
"itemscope",
|
||||
"loop",
|
||||
"multiple",
|
||||
"muted",
|
||||
"nomodule",
|
||||
"novalidate",
|
||||
"open",
|
||||
"playsinline",
|
||||
"readonly",
|
||||
"required",
|
||||
"reversed",
|
||||
"selected",
|
||||
"shadowrootclonable",
|
||||
"shadowrootdelegatesfocus",
|
||||
"shadowrootserializable"
|
||||
]);
|
||||
function isBooleanAttr(attrName) {
|
||||
const booleanAttributes = [
|
||||
"disabled",
|
||||
"checked",
|
||||
"required",
|
||||
"readonly",
|
||||
"open",
|
||||
"selected",
|
||||
"autofocus",
|
||||
"itemscope",
|
||||
"multiple",
|
||||
"novalidate",
|
||||
"allowfullscreen",
|
||||
"allowpaymentrequest",
|
||||
"formnovalidate",
|
||||
"autoplay",
|
||||
"controls",
|
||||
"loop",
|
||||
"muted",
|
||||
"playsinline",
|
||||
"default",
|
||||
"ismap",
|
||||
"reversed",
|
||||
"async",
|
||||
"defer",
|
||||
"nomodule"
|
||||
];
|
||||
return booleanAttributes.includes(attrName);
|
||||
return booleanAttributes.has(attrName);
|
||||
}
|
||||
function attributeShouldntBePreservedIfFalsy(name) {
|
||||
return !["aria-pressed", "aria-checked", "aria-expanded", "aria-selected"].includes(name);
|
||||
|
@ -2776,10 +2775,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
return stores[name];
|
||||
}
|
||||
stores[name] = value;
|
||||
initInterceptors(stores[name]);
|
||||
if (typeof value === "object" && value !== null && value.hasOwnProperty("init") && typeof value.init === "function") {
|
||||
stores[name].init();
|
||||
}
|
||||
initInterceptors(stores[name]);
|
||||
}
|
||||
function getStores() {
|
||||
return stores;
|
||||
|
@ -3070,7 +3069,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
placeInDom(el._x_teleport, target2, modifiers);
|
||||
});
|
||||
};
|
||||
cleanup(() => clone2.remove());
|
||||
cleanup(() => mutateDom(() => {
|
||||
clone2.remove();
|
||||
destroyTree(clone2);
|
||||
}));
|
||||
});
|
||||
var teleportContainerDuringClone = document.createElement("div");
|
||||
function getTarget(expression) {
|
||||
|
@ -3558,7 +3560,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
el._x_lookup = {};
|
||||
effect3(() => loop(el, iteratorNames, evaluateItems, evaluateKey));
|
||||
cleanup(() => {
|
||||
Object.values(el._x_lookup).forEach((el2) => el2.remove());
|
||||
Object.values(el._x_lookup).forEach((el2) => mutateDom(() => {
|
||||
destroyTree(el2);
|
||||
el2.remove();
|
||||
}));
|
||||
delete el._x_prevKeys;
|
||||
delete el._x_lookup;
|
||||
});
|
||||
|
@ -3627,11 +3632,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
for (let i = 0; i < removes.length; i++) {
|
||||
let key = removes[i];
|
||||
if (!!lookup[key]._x_effects) {
|
||||
lookup[key]._x_effects.forEach(dequeueJob);
|
||||
}
|
||||
lookup[key].remove();
|
||||
lookup[key] = null;
|
||||
if (!(key in lookup))
|
||||
continue;
|
||||
mutateDom(() => {
|
||||
destroyTree(lookup[key]);
|
||||
lookup[key].remove();
|
||||
});
|
||||
delete lookup[key];
|
||||
}
|
||||
for (let i = 0; i < moves.length; i++) {
|
||||
|
@ -3752,12 +3758,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
});
|
||||
el._x_currentIfEl = clone2;
|
||||
el._x_undoIf = () => {
|
||||
walk(clone2, (node) => {
|
||||
if (!!node._x_effects) {
|
||||
node._x_effects.forEach(dequeueJob);
|
||||
}
|
||||
mutateDom(() => {
|
||||
destroyTree(clone2);
|
||||
clone2.remove();
|
||||
});
|
||||
clone2.remove();
|
||||
delete el._x_currentIfEl;
|
||||
};
|
||||
return clone2;
|
||||
|
@ -3812,9 +3816,9 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/collapse/dist/module.cjs.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.cjs.js
|
||||
var require_module_cjs2 = __commonJS({
|
||||
"../alpine/packages/collapse/dist/module.cjs.js"(exports, module) {
|
||||
"../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||
|
@ -3887,7 +3891,7 @@ var require_module_cjs2 = __commonJS({
|
|||
start: { height: current + "px" },
|
||||
end: { height: full + "px" }
|
||||
}, () => el._x_isShown = true, () => {
|
||||
if (Math.abs(el.getBoundingClientRect().height - full) < 1) {
|
||||
if (el.getBoundingClientRect().height == full) {
|
||||
el.style.overflow = null;
|
||||
}
|
||||
});
|
||||
|
@ -3933,9 +3937,9 @@ var require_module_cjs2 = __commonJS({
|
|||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/focus/dist/module.cjs.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.cjs.js
|
||||
var require_module_cjs3 = __commonJS({
|
||||
"../alpine/packages/focus/dist/module.cjs.js"(exports, module) {
|
||||
"../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.cjs.js"(exports, module) {
|
||||
var __create2 = Object.create;
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
|
@ -4935,9 +4939,9 @@ var require_module_cjs3 = __commonJS({
|
|||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/persist/dist/module.cjs.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.cjs.js
|
||||
var require_module_cjs4 = __commonJS({
|
||||
"../alpine/packages/persist/dist/module.cjs.js"(exports, module) {
|
||||
"../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||
|
@ -5024,9 +5028,9 @@ var require_module_cjs4 = __commonJS({
|
|||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/intersect/dist/module.cjs.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.cjs.js
|
||||
var require_module_cjs5 = __commonJS({
|
||||
"../alpine/packages/intersect/dist/module.cjs.js"(exports, module) {
|
||||
"../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||
|
@ -5106,8 +5110,80 @@ var require_module_cjs5 = __commonJS({
|
|||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/anchor/dist/module.cjs.js
|
||||
// node_modules/@alpinejs/resize/dist/module.cjs.js
|
||||
var require_module_cjs6 = __commonJS({
|
||||
"node_modules/@alpinejs/resize/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all2) => {
|
||||
for (var name in all2)
|
||||
__defProp2(target, name, { get: all2[name], enumerable: true });
|
||||
};
|
||||
var __copyProps2 = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames2(from))
|
||||
if (!__hasOwnProp2.call(to, key) && key !== except)
|
||||
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
||||
var module_exports = {};
|
||||
__export(module_exports, {
|
||||
default: () => module_default,
|
||||
resize: () => src_default
|
||||
});
|
||||
module.exports = __toCommonJS(module_exports);
|
||||
function src_default(Alpine19) {
|
||||
Alpine19.directive("resize", Alpine19.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => {
|
||||
let evaluator = evaluateLater(expression);
|
||||
let evaluate = (width, height) => {
|
||||
evaluator(() => {
|
||||
}, { scope: { "$width": width, "$height": height } });
|
||||
};
|
||||
let off = modifiers.includes("document") ? onDocumentResize(evaluate) : onElResize(el, evaluate);
|
||||
cleanup(() => off());
|
||||
}));
|
||||
}
|
||||
function onElResize(el, callback) {
|
||||
let observer = new ResizeObserver((entries) => {
|
||||
let [width, height] = dimensions(entries);
|
||||
callback(width, height);
|
||||
});
|
||||
observer.observe(el);
|
||||
return () => observer.disconnect();
|
||||
}
|
||||
var documentResizeObserver;
|
||||
var documentResizeObserverCallbacks = /* @__PURE__ */ new Set();
|
||||
function onDocumentResize(callback) {
|
||||
documentResizeObserverCallbacks.add(callback);
|
||||
if (!documentResizeObserver) {
|
||||
documentResizeObserver = new ResizeObserver((entries) => {
|
||||
let [width, height] = dimensions(entries);
|
||||
documentResizeObserverCallbacks.forEach((i) => i(width, height));
|
||||
});
|
||||
documentResizeObserver.observe(document.documentElement);
|
||||
}
|
||||
return () => {
|
||||
documentResizeObserverCallbacks.delete(callback);
|
||||
};
|
||||
}
|
||||
function dimensions(entries) {
|
||||
let width, height;
|
||||
for (let entry of entries) {
|
||||
width = entry.borderBoxSize[0].inlineSize;
|
||||
height = entry.borderBoxSize[0].blockSize;
|
||||
}
|
||||
return [width, height];
|
||||
}
|
||||
var module_default = src_default;
|
||||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/anchor/dist/module.cjs.js
|
||||
var require_module_cjs7 = __commonJS({
|
||||
"../alpine/packages/anchor/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
|
@ -6645,7 +6721,7 @@ var require_nprogress = __commonJS({
|
|||
});
|
||||
|
||||
// ../alpine/packages/morph/dist/module.cjs.js
|
||||
var require_module_cjs7 = __commonJS({
|
||||
var require_module_cjs8 = __commonJS({
|
||||
"../alpine/packages/morph/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
|
@ -6744,6 +6820,8 @@ var require_module_cjs7 = __commonJS({
|
|||
let toAttributes = Array.from(to.attributes);
|
||||
for (let i = domAttributes.length - 1; i >= 0; i--) {
|
||||
let name = domAttributes[i].name;
|
||||
if (name === "style")
|
||||
continue;
|
||||
if (!to.hasAttribute(name)) {
|
||||
from2.removeAttribute(name);
|
||||
}
|
||||
|
@ -6751,6 +6829,8 @@ var require_module_cjs7 = __commonJS({
|
|||
for (let i = toAttributes.length - 1; i >= 0; i--) {
|
||||
let name = toAttributes[i].name;
|
||||
let value = toAttributes[i].value;
|
||||
if (name === "style")
|
||||
continue;
|
||||
if (from2.getAttribute(name) !== value) {
|
||||
from2.setAttribute(name, value);
|
||||
}
|
||||
|
@ -7006,9 +7086,9 @@ var require_module_cjs7 = __commonJS({
|
|||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/mask/dist/module.cjs.js
|
||||
var require_module_cjs8 = __commonJS({
|
||||
"../alpine/packages/mask/dist/module.cjs.js"(exports, module) {
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.cjs.js
|
||||
var require_module_cjs9 = __commonJS({
|
||||
"../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||
|
@ -8509,7 +8589,8 @@ var import_collapse = __toESM(require_module_cjs2());
|
|||
var import_focus = __toESM(require_module_cjs3());
|
||||
var import_persist2 = __toESM(require_module_cjs4());
|
||||
var import_intersect = __toESM(require_module_cjs5());
|
||||
var import_anchor = __toESM(require_module_cjs6());
|
||||
var import_resize = __toESM(require_module_cjs6());
|
||||
var import_anchor = __toESM(require_module_cjs7());
|
||||
|
||||
// js/plugins/navigate/history.js
|
||||
var Snapshot = class {
|
||||
|
@ -8660,7 +8741,7 @@ function extractDestinationFromLink(linkEl) {
|
|||
return createUrlObjectFromString(linkEl.getAttribute("href"));
|
||||
}
|
||||
function createUrlObjectFromString(urlString) {
|
||||
return new URL(urlString, document.baseURI);
|
||||
return urlString !== null && new URL(urlString, document.baseURI);
|
||||
}
|
||||
function getUriStringFromUrlObject(urlObject) {
|
||||
return urlObject.pathname + urlObject.search + urlObject.hash;
|
||||
|
@ -8782,8 +8863,10 @@ function restoreScrollPositionOrScrollToTop() {
|
|||
}
|
||||
};
|
||||
queueMicrotask(() => {
|
||||
scroll(document.body);
|
||||
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
|
||||
queueMicrotask(() => {
|
||||
scroll(document.body);
|
||||
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -9087,12 +9170,16 @@ function navigate_default(Alpine19) {
|
|||
let shouldPrefetchOnHover = modifiers.includes("hover");
|
||||
shouldPrefetchOnHover && whenThisLinkIsHoveredFor(el, 60, () => {
|
||||
let destination = extractDestinationFromLink(el);
|
||||
if (!destination)
|
||||
return;
|
||||
prefetchHtml(destination, (html, finalDestination) => {
|
||||
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
|
||||
});
|
||||
});
|
||||
whenThisLinkIsPressed(el, (whenItIsReleased) => {
|
||||
let destination = extractDestinationFromLink(el);
|
||||
if (!destination)
|
||||
return;
|
||||
prefetchHtml(destination, (html, finalDestination) => {
|
||||
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
|
||||
});
|
||||
|
@ -9441,8 +9528,8 @@ function fromQueryString(search) {
|
|||
}
|
||||
|
||||
// js/lifecycle.js
|
||||
var import_morph = __toESM(require_module_cjs7());
|
||||
var import_mask = __toESM(require_module_cjs8());
|
||||
var import_morph = __toESM(require_module_cjs8());
|
||||
var import_mask = __toESM(require_module_cjs9());
|
||||
var import_alpinejs5 = __toESM(require_module_cjs());
|
||||
function start() {
|
||||
setTimeout(() => ensureLivewireScriptIsntMisplaced());
|
||||
|
@ -9451,6 +9538,7 @@ function start() {
|
|||
import_alpinejs5.default.plugin(import_morph.default);
|
||||
import_alpinejs5.default.plugin(history2);
|
||||
import_alpinejs5.default.plugin(import_intersect.default);
|
||||
import_alpinejs5.default.plugin(import_resize.default);
|
||||
import_alpinejs5.default.plugin(import_collapse.default);
|
||||
import_alpinejs5.default.plugin(import_anchor.default);
|
||||
import_alpinejs5.default.plugin(import_focus.default);
|
||||
|
|
209
public/vendor/livewire/livewire.js
vendored
209
public/vendor/livewire/livewire.js
vendored
|
@ -851,10 +851,9 @@
|
|||
});
|
||||
}
|
||||
function cleanupElement(el) {
|
||||
if (el._x_cleanups) {
|
||||
while (el._x_cleanups.length)
|
||||
el._x_cleanups.pop()();
|
||||
}
|
||||
el._x_effects?.forEach(dequeueJob);
|
||||
while (el._x_cleanups?.length)
|
||||
el._x_cleanups.pop()();
|
||||
}
|
||||
var observer = new MutationObserver(onMutate);
|
||||
var currentlyObserving = false;
|
||||
|
@ -1092,27 +1091,23 @@
|
|||
magics[name] = callback;
|
||||
}
|
||||
function injectMagics(obj, el) {
|
||||
let memoizedUtilities = getUtilities(el);
|
||||
Object.entries(magics).forEach(([name, callback]) => {
|
||||
let memoizedUtilities = null;
|
||||
function getUtilities() {
|
||||
if (memoizedUtilities) {
|
||||
return memoizedUtilities;
|
||||
} else {
|
||||
let [utilities, cleanup2] = getElementBoundUtilities(el);
|
||||
memoizedUtilities = { interceptor, ...utilities };
|
||||
onElRemoved(el, cleanup2);
|
||||
return memoizedUtilities;
|
||||
}
|
||||
}
|
||||
Object.defineProperty(obj, `$${name}`, {
|
||||
get() {
|
||||
return callback(el, getUtilities());
|
||||
return callback(el, memoizedUtilities);
|
||||
},
|
||||
enumerable: false
|
||||
});
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
function getUtilities(el) {
|
||||
let [utilities, cleanup2] = getElementBoundUtilities(el);
|
||||
let utils = { interceptor, ...utilities };
|
||||
onElRemoved(el, cleanup2);
|
||||
return utils;
|
||||
}
|
||||
function tryCatch(el, expression, callback, ...args) {
|
||||
try {
|
||||
return callback(...args);
|
||||
|
@ -1486,8 +1481,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
function destroyTree(root, walker = walk) {
|
||||
walker(root, (el) => {
|
||||
cleanupAttributes(el);
|
||||
cleanupElement(el);
|
||||
cleanupAttributes(el);
|
||||
});
|
||||
}
|
||||
function warnAboutMissingPlugins() {
|
||||
|
@ -2067,34 +2062,37 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
return rawValue ? Boolean(rawValue) : null;
|
||||
}
|
||||
var booleanAttributes = /* @__PURE__ */ new Set([
|
||||
"allowfullscreen",
|
||||
"async",
|
||||
"autofocus",
|
||||
"autoplay",
|
||||
"checked",
|
||||
"controls",
|
||||
"default",
|
||||
"defer",
|
||||
"disabled",
|
||||
"formnovalidate",
|
||||
"inert",
|
||||
"ismap",
|
||||
"itemscope",
|
||||
"loop",
|
||||
"multiple",
|
||||
"muted",
|
||||
"nomodule",
|
||||
"novalidate",
|
||||
"open",
|
||||
"playsinline",
|
||||
"readonly",
|
||||
"required",
|
||||
"reversed",
|
||||
"selected",
|
||||
"shadowrootclonable",
|
||||
"shadowrootdelegatesfocus",
|
||||
"shadowrootserializable"
|
||||
]);
|
||||
function isBooleanAttr(attrName) {
|
||||
const booleanAttributes = [
|
||||
"disabled",
|
||||
"checked",
|
||||
"required",
|
||||
"readonly",
|
||||
"open",
|
||||
"selected",
|
||||
"autofocus",
|
||||
"itemscope",
|
||||
"multiple",
|
||||
"novalidate",
|
||||
"allowfullscreen",
|
||||
"allowpaymentrequest",
|
||||
"formnovalidate",
|
||||
"autoplay",
|
||||
"controls",
|
||||
"loop",
|
||||
"muted",
|
||||
"playsinline",
|
||||
"default",
|
||||
"ismap",
|
||||
"reversed",
|
||||
"async",
|
||||
"defer",
|
||||
"nomodule"
|
||||
];
|
||||
return booleanAttributes.includes(attrName);
|
||||
return booleanAttributes.has(attrName);
|
||||
}
|
||||
function attributeShouldntBePreservedIfFalsy(name) {
|
||||
return !["aria-pressed", "aria-checked", "aria-expanded", "aria-selected"].includes(name);
|
||||
|
@ -2195,10 +2193,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
return stores[name];
|
||||
}
|
||||
stores[name] = value;
|
||||
initInterceptors(stores[name]);
|
||||
if (typeof value === "object" && value !== null && value.hasOwnProperty("init") && typeof value.init === "function") {
|
||||
stores[name].init();
|
||||
}
|
||||
initInterceptors(stores[name]);
|
||||
}
|
||||
function getStores() {
|
||||
return stores;
|
||||
|
@ -3136,7 +3134,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
placeInDom(el._x_teleport, target2, modifiers);
|
||||
});
|
||||
};
|
||||
cleanup2(() => clone2.remove());
|
||||
cleanup2(() => mutateDom(() => {
|
||||
clone2.remove();
|
||||
destroyTree(clone2);
|
||||
}));
|
||||
});
|
||||
var teleportContainerDuringClone = document.createElement("div");
|
||||
function getTarget(expression) {
|
||||
|
@ -3624,7 +3625,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
el._x_lookup = {};
|
||||
effect3(() => loop(el, iteratorNames, evaluateItems, evaluateKey));
|
||||
cleanup2(() => {
|
||||
Object.values(el._x_lookup).forEach((el2) => el2.remove());
|
||||
Object.values(el._x_lookup).forEach((el2) => mutateDom(() => {
|
||||
destroyTree(el2);
|
||||
el2.remove();
|
||||
}));
|
||||
delete el._x_prevKeys;
|
||||
delete el._x_lookup;
|
||||
});
|
||||
|
@ -3693,11 +3697,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
for (let i = 0; i < removes.length; i++) {
|
||||
let key = removes[i];
|
||||
if (!!lookup[key]._x_effects) {
|
||||
lookup[key]._x_effects.forEach(dequeueJob);
|
||||
}
|
||||
lookup[key].remove();
|
||||
lookup[key] = null;
|
||||
if (!(key in lookup))
|
||||
continue;
|
||||
mutateDom(() => {
|
||||
destroyTree(lookup[key]);
|
||||
lookup[key].remove();
|
||||
});
|
||||
delete lookup[key];
|
||||
}
|
||||
for (let i = 0; i < moves.length; i++) {
|
||||
|
@ -3818,12 +3823,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
});
|
||||
el._x_currentIfEl = clone2;
|
||||
el._x_undoIf = () => {
|
||||
walk(clone2, (node) => {
|
||||
if (!!node._x_effects) {
|
||||
node._x_effects.forEach(dequeueJob);
|
||||
}
|
||||
mutateDom(() => {
|
||||
destroyTree(clone2);
|
||||
clone2.remove();
|
||||
});
|
||||
clone2.remove();
|
||||
delete el._x_currentIfEl;
|
||||
};
|
||||
return clone2;
|
||||
|
@ -4762,7 +4765,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
};
|
||||
|
||||
// ../alpine/packages/collapse/dist/module.esm.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.esm.js
|
||||
function src_default2(Alpine3) {
|
||||
Alpine3.directive("collapse", collapse);
|
||||
collapse.inline = (el, { modifiers }) => {
|
||||
|
@ -4812,7 +4815,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
start: { height: current + "px" },
|
||||
end: { height: full + "px" }
|
||||
}, () => el._x_isShown = true, () => {
|
||||
if (Math.abs(el.getBoundingClientRect().height - full) < 1) {
|
||||
if (el.getBoundingClientRect().height == full) {
|
||||
el.style.overflow = null;
|
||||
}
|
||||
});
|
||||
|
@ -4856,7 +4859,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
var module_default2 = src_default2;
|
||||
|
||||
// ../alpine/packages/focus/dist/module.esm.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.esm.js
|
||||
var candidateSelectors = ["input", "select", "textarea", "a[href]", "button", "[tabindex]:not(slot)", "audio[controls]", "video[controls]", '[contenteditable]:not([contenteditable="false"])', "details>summary:first-of-type", "details"];
|
||||
var candidateSelector = /* @__PURE__ */ candidateSelectors.join(",");
|
||||
var NoElement = typeof Element === "undefined";
|
||||
|
@ -5805,7 +5808,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
var module_default3 = src_default3;
|
||||
|
||||
// ../alpine/packages/persist/dist/module.esm.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.esm.js
|
||||
function src_default4(Alpine3) {
|
||||
let persist = () => {
|
||||
let alias;
|
||||
|
@ -5867,7 +5870,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
var module_default4 = src_default4;
|
||||
|
||||
// ../alpine/packages/intersect/dist/module.esm.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.esm.js
|
||||
function src_default5(Alpine3) {
|
||||
Alpine3.directive("intersect", Alpine3.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
||||
let evaluate3 = evaluateLater2(expression);
|
||||
|
@ -5922,6 +5925,51 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
var module_default5 = src_default5;
|
||||
|
||||
// node_modules/@alpinejs/resize/dist/module.esm.js
|
||||
function src_default6(Alpine3) {
|
||||
Alpine3.directive("resize", Alpine3.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
||||
let evaluator = evaluateLater2(expression);
|
||||
let evaluate3 = (width, height) => {
|
||||
evaluator(() => {
|
||||
}, { scope: { "$width": width, "$height": height } });
|
||||
};
|
||||
let off = modifiers.includes("document") ? onDocumentResize(evaluate3) : onElResize(el, evaluate3);
|
||||
cleanup2(() => off());
|
||||
}));
|
||||
}
|
||||
function onElResize(el, callback) {
|
||||
let observer2 = new ResizeObserver((entries) => {
|
||||
let [width, height] = dimensions(entries);
|
||||
callback(width, height);
|
||||
});
|
||||
observer2.observe(el);
|
||||
return () => observer2.disconnect();
|
||||
}
|
||||
var documentResizeObserver;
|
||||
var documentResizeObserverCallbacks = /* @__PURE__ */ new Set();
|
||||
function onDocumentResize(callback) {
|
||||
documentResizeObserverCallbacks.add(callback);
|
||||
if (!documentResizeObserver) {
|
||||
documentResizeObserver = new ResizeObserver((entries) => {
|
||||
let [width, height] = dimensions(entries);
|
||||
documentResizeObserverCallbacks.forEach((i) => i(width, height));
|
||||
});
|
||||
documentResizeObserver.observe(document.documentElement);
|
||||
}
|
||||
return () => {
|
||||
documentResizeObserverCallbacks.delete(callback);
|
||||
};
|
||||
}
|
||||
function dimensions(entries) {
|
||||
let width, height;
|
||||
for (let entry of entries) {
|
||||
width = entry.borderBoxSize[0].inlineSize;
|
||||
height = entry.borderBoxSize[0].blockSize;
|
||||
}
|
||||
return [width, height];
|
||||
}
|
||||
var module_default6 = src_default6;
|
||||
|
||||
// ../alpine/packages/anchor/dist/module.esm.js
|
||||
var min = Math.min;
|
||||
var max = Math.max;
|
||||
|
@ -7096,7 +7144,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
platform: platformWithCache
|
||||
});
|
||||
};
|
||||
function src_default6(Alpine3) {
|
||||
function src_default7(Alpine3) {
|
||||
Alpine3.magic("anchor", (el) => {
|
||||
if (!el._x_anchor)
|
||||
throw "Alpine: No x-anchor directive found on element using $anchor...";
|
||||
|
@ -7154,7 +7202,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
let unstyled = modifiers.includes("no-style");
|
||||
return { placement, offsetValue, unstyled };
|
||||
}
|
||||
var module_default6 = src_default6;
|
||||
var module_default7 = src_default7;
|
||||
|
||||
// js/plugins/navigate/history.js
|
||||
var Snapshot = class {
|
||||
|
@ -7305,7 +7353,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
return createUrlObjectFromString(linkEl.getAttribute("href"));
|
||||
}
|
||||
function createUrlObjectFromString(urlString) {
|
||||
return new URL(urlString, document.baseURI);
|
||||
return urlString !== null && new URL(urlString, document.baseURI);
|
||||
}
|
||||
function getUriStringFromUrlObject(urlObject) {
|
||||
return urlObject.pathname + urlObject.search + urlObject.hash;
|
||||
|
@ -7426,8 +7474,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
}
|
||||
};
|
||||
queueMicrotask(() => {
|
||||
scroll(document.body);
|
||||
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
|
||||
queueMicrotask(() => {
|
||||
scroll(document.body);
|
||||
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -7730,12 +7780,16 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
let shouldPrefetchOnHover = modifiers.includes("hover");
|
||||
shouldPrefetchOnHover && whenThisLinkIsHoveredFor(el, 60, () => {
|
||||
let destination = extractDestinationFromLink(el);
|
||||
if (!destination)
|
||||
return;
|
||||
prefetchHtml(destination, (html, finalDestination) => {
|
||||
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
|
||||
});
|
||||
});
|
||||
whenThisLinkIsPressed(el, (whenItIsReleased) => {
|
||||
let destination = extractDestinationFromLink(el);
|
||||
if (!destination)
|
||||
return;
|
||||
prefetchHtml(destination, (html, finalDestination) => {
|
||||
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
|
||||
});
|
||||
|
@ -8158,6 +8212,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
let toAttributes = Array.from(to.attributes);
|
||||
for (let i = domAttributes.length - 1; i >= 0; i--) {
|
||||
let name = domAttributes[i].name;
|
||||
if (name === "style")
|
||||
continue;
|
||||
if (!to.hasAttribute(name)) {
|
||||
from2.removeAttribute(name);
|
||||
}
|
||||
|
@ -8165,6 +8221,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
for (let i = toAttributes.length - 1; i >= 0; i--) {
|
||||
let name = toAttributes[i].name;
|
||||
let value = toAttributes[i].value;
|
||||
if (name === "style")
|
||||
continue;
|
||||
if (from2.getAttribute(name) !== value) {
|
||||
from2.setAttribute(name, value);
|
||||
}
|
||||
|
@ -8413,13 +8471,13 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
to.setAttribute("id", fromId);
|
||||
to.id = fromId;
|
||||
}
|
||||
function src_default7(Alpine3) {
|
||||
function src_default8(Alpine3) {
|
||||
Alpine3.morph = morph;
|
||||
}
|
||||
var module_default7 = src_default7;
|
||||
var module_default8 = src_default8;
|
||||
|
||||
// ../alpine/packages/mask/dist/module.esm.js
|
||||
function src_default8(Alpine3) {
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.esm.js
|
||||
function src_default9(Alpine3) {
|
||||
Alpine3.directive("mask", (el, { value, expression }, { effect: effect3, evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
||||
let templateFn = () => expression;
|
||||
let lastInputValue = "";
|
||||
|
@ -8581,22 +8639,23 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||
});
|
||||
return template;
|
||||
}
|
||||
var module_default8 = src_default8;
|
||||
var module_default9 = src_default9;
|
||||
|
||||
// js/lifecycle.js
|
||||
function start2() {
|
||||
setTimeout(() => ensureLivewireScriptIsntMisplaced());
|
||||
dispatch(document, "livewire:init");
|
||||
dispatch(document, "livewire:initializing");
|
||||
module_default.plugin(module_default7);
|
||||
module_default.plugin(module_default8);
|
||||
module_default.plugin(history2);
|
||||
module_default.plugin(module_default5);
|
||||
module_default.plugin(module_default2);
|
||||
module_default.plugin(module_default6);
|
||||
module_default.plugin(module_default2);
|
||||
module_default.plugin(module_default7);
|
||||
module_default.plugin(module_default3);
|
||||
module_default.plugin(module_default4);
|
||||
module_default.plugin(navigate_default);
|
||||
module_default.plugin(module_default8);
|
||||
module_default.plugin(module_default9);
|
||||
module_default.addRootSelector(() => "[wire\\:id]");
|
||||
module_default.onAttributesAdded((el, attributes) => {
|
||||
if (!Array.from(attributes).some((attribute) => matchesForLivewireDirective(attribute.name)))
|
||||
|
|
12
public/vendor/livewire/livewire.min.js
vendored
12
public/vendor/livewire/livewire.min.js
vendored
File diff suppressed because one or more lines are too long
6
public/vendor/livewire/livewire.min.js.map
vendored
6
public/vendor/livewire/livewire.min.js.map
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/livewire/manifest.json
vendored
2
public/vendor/livewire/manifest.json
vendored
|
@ -1,2 +1,2 @@
|
|||
|
||||
{"/livewire.js":"87e1046f"}
|
||||
{"/livewire.js":"923613aa"}
|
||||
|
|
|
@ -621,31 +621,30 @@ h4 {
|
|||
//border-left: 1px solid #dddddd;
|
||||
//border-right: 1px solid #dddddd;
|
||||
display: table;
|
||||
|
||||
}
|
||||
|
||||
.row-striped .row:nth-of-type(odd) div {
|
||||
background-color: #f9f9f9;
|
||||
border-top: 1px solid #dddddd;
|
||||
display: table-cell;
|
||||
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.row-striped .row:nth-of-type(even) div {
|
||||
background: #FFFFFF;
|
||||
border-top: 1px solid #dddddd;
|
||||
display: table-cell;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
|
||||
.row-new-striped {
|
||||
vertical-align: top;
|
||||
line-height: 2.6;
|
||||
padding: 0px;
|
||||
margin-left: 20px;
|
||||
padding: 3px;
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding-right: 20px;
|
||||
word-wrap: break-word;
|
||||
table-layout:fixed;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -656,25 +655,28 @@ h4 {
|
|||
.row-new-striped > .row:nth-of-type(even) {
|
||||
background: #FFFFFF;
|
||||
border-top: 1px solid #dddddd;
|
||||
line-height: 1.9;
|
||||
display: table-row;
|
||||
|
||||
}
|
||||
|
||||
.row-new-striped > .row:nth-of-type(odd) {
|
||||
background-color: #F8F8F8;
|
||||
border-top: 1px solid #dddddd;
|
||||
display: table-row;
|
||||
|
||||
line-height: 1.9;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.row-new-striped div {
|
||||
display: table-cell;
|
||||
border-top: 1px solid #dddddd;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.row-new-striped div {
|
||||
display: table-cell;
|
||||
border-top: 1px solid #dddddd;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -387,5 +387,6 @@ return [
|
|||
'restore_default_avatar_help' => '',
|
||||
'due_checkin_days' => 'Due For Checkin Warning',
|
||||
'due_checkin_days_help' => 'How many days before the expected checkin of an asset should it be listed in the "Due for checkin" page?',
|
||||
'no_groups' => 'No groups have been created yet. Visit <code>Admin Settings > Permission Groups</code> to add one.',
|
||||
|
||||
];
|
||||
|
|
|
@ -173,6 +173,7 @@ return [
|
|||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Validation Language Lines
|
||||
|
@ -194,7 +195,7 @@ return [
|
|||
'custom_field_not_found_on_model' => 'This field seems to exist, but is not available on this Asset Model\'s fieldset.',
|
||||
|
||||
// date_format validation with slightly less stupid messages. It duplicates a lot, but it gets the job done :(
|
||||
// We use this because the default error message for date_format is reflects php Y-m-d, which non-PHP
|
||||
// We use this because the default error message for date_format reflects php Y-m-d, which non-PHP
|
||||
// people won't know how to format.
|
||||
'purchase_date.date_format' => 'The :attribute must be a valid date in YYYY-MM-DD format',
|
||||
'last_audit_date.date_format' => 'The :attribute must be a valid date in YYYY-MM-DD hh:mm:ss format',
|
||||
|
@ -206,6 +207,13 @@ return [
|
|||
'checkboxes' => ':attribute contains invalid options.',
|
||||
'radio_buttons' => ':attribute is invalid.',
|
||||
'invalid_value_in_field' => 'Invalid value included in this field',
|
||||
|
||||
'ldap_username_field' => [
|
||||
'not_in' => '<code>sAMAccountName</code> (mixed case) will likely not work. You should use <code>samaccountname</code> (lowercase) instead.'
|
||||
],
|
||||
'ldap_auth_filter_query' => ['not_in' => '<code>uid=samaccountname</code> is probably not a valid auth filter. You probably want <code>uid=</code> '],
|
||||
'ldap_filter' => ['regex' => 'This value should probably not be wrapped in parentheses.'],
|
||||
|
||||
],
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
@ -301,7 +301,7 @@
|
|||
{{ trans('general.notes') }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="col-md-9" style="word-wrap: break-word;">
|
||||
{!! nl2br(Helper::parseEscapedMarkedownInline($accessory->notes)) !!}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -103,20 +103,23 @@
|
|||
</div>
|
||||
@can('self.profile')
|
||||
<div class="col-md-12">
|
||||
<a href="{{ route('profile') }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print">
|
||||
<a href="{{ route('profile') }}" style="width: 100%;" class="btn btn-sm btn-warning btn-social btn-block hidden-print">
|
||||
<x-icon type="edit" />
|
||||
{{ trans('general.editprofile') }}
|
||||
</a>
|
||||
</div>
|
||||
@endcan
|
||||
<div class="col-md-12" style="padding-top: 5px;">
|
||||
<a href="{{ route('account.password.index') }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print" target="_blank" rel="noopener">
|
||||
<a href="{{ route('account.password.index') }}" style="width: 100%;" class="btn btn-sm btn-primary btn-social btn-block hidden-print" target="_blank" rel="noopener">
|
||||
<x-icon type="password" class="fa-fw" />
|
||||
{{ trans('general.changepassword') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@can('self.api')
|
||||
<div class="col-md-12" style="padding-top: 5px;">
|
||||
<a href="{{ route('user.api') }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print" target="_blank" rel="noopener">
|
||||
<a href="{{ route('user.api') }}" style="width: 100%;" class="btn btn-sm btn-primary btn-social btn-block hidden-print" target="_blank" rel="noopener">
|
||||
<x-icon type="api-key" class="fa-fw" />
|
||||
{{ trans('general.manage_api_keys') }}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -124,7 +127,8 @@
|
|||
|
||||
|
||||
<div class="col-md-12" style="padding-top: 5px;">
|
||||
<a href="{{ route('profile.print') }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print" target="_blank" rel="noopener">
|
||||
<a href="{{ route('profile.print') }}" style="width: 100%;" class="btn btn-sm btn-primary btn-social btn-block hidden-print" target="_blank" rel="noopener">
|
||||
<x-icon type="print" class="fa-fw" />
|
||||
{{ trans('admin/users/general.print_assigned') }}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -134,10 +138,16 @@
|
|||
@if (!empty($user->email))
|
||||
<form action="{{ route('profile.email_assets') }}" method="POST">
|
||||
{{ csrf_field() }}
|
||||
<button style="width: 100%;" class="btn btn-sm btn-primary hidden-print" rel="noopener">{{ trans('admin/users/general.email_assigned') }}</button>
|
||||
<button style="width: 100%;" class="btn btn-sm btn-primary btn-social btn-block hidden-print" rel="noopener">
|
||||
<x-icon type="email" class="fa-fw" />
|
||||
{{ trans('admin/users/general.email_assigned') }}
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<button style="width: 100%;" class="btn btn-sm btn-primary hidden-print" rel="noopener" disabled title="{{ trans('admin/users/message.user_has_no_email') }}">{{ trans('admin/users/general.email_assigned') }}</button>
|
||||
<button style="width: 100%;" class="btn btn-sm btn-primary btn-social btn-block hidden-print disabled" rel="noopener" disabled title="{{ trans('admin/users/message.user_has_no_email') }}">
|
||||
<x-icon type="email" class="fa-fw" />
|
||||
{{ trans('admin/users/general.email_assigned') }}
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
'required' => true,
|
||||
'asset_status_type' => 'RTD',
|
||||
'select_id' => 'assigned_assets_select',
|
||||
'asset_selector_div_id' => 'assets_to_checkout_div',
|
||||
'asset_ids' => old('selected_assets')
|
||||
])
|
||||
|
||||
|
||||
|
@ -42,7 +44,7 @@
|
|||
@include ('partials.forms.checkout-selector', ['user_select' => 'true','asset_select' => 'true', 'location_select' => 'true'])
|
||||
|
||||
@include ('partials.forms.edit.user-select', ['translated_name' => trans('general.user'), 'fieldname' => 'assigned_user'])
|
||||
@include ('partials.forms.edit.asset-select', ['translated_name' => trans('general.asset'), 'fieldname' => 'assigned_asset', 'unselect' => 'true', 'style' => 'display:none;'])
|
||||
@include ('partials.forms.edit.asset-select', ['translated_name' => trans('general.asset'), 'asset_selector_div_id' => 'assigned_asset', 'fieldname' => 'assigned_asset', 'unselect' => 'true', 'style' => 'display:none;'])
|
||||
@include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'assigned_location', 'style' => 'display:none;'])
|
||||
|
||||
<!-- Checkout/Checkin Date -->
|
||||
|
|
|
@ -189,14 +189,14 @@ dir="{{ Helper::determineLanguageDirection() }}">
|
|||
action="{{ route('findbytag/hardware') }}" method="get">
|
||||
<div class="col-xs-12 col-md-12">
|
||||
<div class="col-xs-12 form-group">
|
||||
<label class="sr-only"
|
||||
for="tagSearch">{{ trans('general.lookup_by_tag') }}</label>
|
||||
<input type="text" class="form-control" id="tagSearch" name="assetTag"
|
||||
placeholder="{{ trans('general.lookup_by_tag') }}">
|
||||
<label class="sr-only" for="tagSearch">
|
||||
{{ trans('general.lookup_by_tag') }}
|
||||
</label>
|
||||
<input type="text" class="form-control" id="tagSearch" name="assetTag" placeholder="{{ trans('general.lookup_by_tag') }}">
|
||||
<input type="hidden" name="topsearch" value="true" id="search">
|
||||
</div>
|
||||
<div class="col-xs-1">
|
||||
<button type="submit" class="btn btn-primary pull-right">
|
||||
<button type="submit" id="topSearchButton" class="btn btn-primary pull-right">
|
||||
<x-icon type="search" />
|
||||
<span class="sr-only">{{ trans('general.search') }}</span>
|
||||
</button>
|
||||
|
|
|
@ -25,9 +25,15 @@
|
|||
<label for="currency" class="col-md-3 control-label">
|
||||
{{ trans('admin/locations/table.currency') }}
|
||||
</label>
|
||||
<div class="col-md-9">
|
||||
{{ Form::text('currency', old('currency', $item->currency), array('class' => 'form-control','placeholder' => 'USD', 'maxlength'=>'3', 'style'=>'width: 60px;', 'aria-label'=>'currency', 'required' => (Helper::checkIfRequired($item, 'currency')) ? true : '')) }}
|
||||
{!! $errors->first('currency', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<div class="col-md-7">
|
||||
<input class="form-control" style="width:100px" type="text" name="currency" aria-label="currency" id="currency" value="{{ old('currency', $item->currency) }}"{!! (Helper::checkIfRequired($item, 'currency')) ? ' required' : '' !!} maxlength="3" />
|
||||
@error('currency')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -40,8 +46,13 @@
|
|||
{{ trans('admin/locations/table.ldap_ou') }}
|
||||
</label>
|
||||
<div class="col-md-7">
|
||||
{{ Form::text('ldap_ou', old('ldap_ou', $item->ldap_ou), array('class' => 'form-control', 'required' => (Helper::checkIfRequired($item, 'ldap_ou')) ? true : '')) }}
|
||||
{!! $errors->first('ldap_ou', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<input class="form-control" type="text" name="ldap_ou" aria-label="ldap_ou" id="ldap_ou" value="{{ old('ldap_ou', $item->ldap_ou) }}"{!! (Helper::checkIfRequired($item, 'ldap_ou')) ? ' required' : '' !!} maxlength="191" />
|
||||
@error('ldap_ou')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<!-- Asset -->
|
||||
<div id="assigned_asset" class="form-group{{ $errors->has($fieldname) ? ' has-error' : '' }}"{!! (isset($style)) ? ' style="'.e($style).'"' : '' !!}>
|
||||
<div id="{{ $asset_selector_div_id ?? "assigned_asset" }}"
|
||||
class="form-group{{ $errors->has($fieldname) ? ' has-error' : '' }}"{!! (isset($style)) ? ' style="'.e($style).'"' : '' !!}>
|
||||
{{ Form::label($fieldname, $translated_name, array('class' => 'col-md-3 control-label')) }}
|
||||
<div class="col-md-7">
|
||||
<select class="js-data-ajax select2" data-endpoint="hardware" data-placeholder="{{ trans('general.select_asset') }}" aria-label="{{ $fieldname }}" name="{{ $fieldname }}" style="width: 100%" id="{{ (isset($select_id)) ? $select_id : 'assigned_asset_select' }}"{{ (isset($multiple)) ? ' multiple' : '' }}{!! (!empty($asset_status_type)) ? ' data-asset-status-type="' . $asset_status_type . '"' : '' !!}{{ ((isset($required) && ($required =='true'))) ? ' required' : '' }}>
|
||||
|
@ -11,6 +12,15 @@
|
|||
@else
|
||||
@if(!isset($multiple))
|
||||
<option value="" role="option">{{ trans('general.select_asset') }}</option>
|
||||
@else
|
||||
@if(isset($asset_ids))
|
||||
@foreach($asset_ids as $asset_id)
|
||||
<option value="{{ $asset_id }}" selected="selected" role="option" aria-selected="true"
|
||||
role="option">
|
||||
{{ (\App\Models\Asset::find($asset_id)) ? \App\Models\Asset::find($asset_id)->present()->fullName : '' }}
|
||||
</option>
|
||||
@endforeach
|
||||
@endif
|
||||
@endif
|
||||
@endif
|
||||
</select>
|
||||
|
|
|
@ -21,9 +21,10 @@
|
|||
</style>
|
||||
|
||||
|
||||
{{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'off', 'class' => 'form-horizontal', 'role' => 'form' ]) }}
|
||||
<form method="POST" action="{{ route('settings.alerts.save') }}" autocomplete="off" class="form-horizontal" role="form" id="create-form">
|
||||
|
||||
<!-- CSRF Token -->
|
||||
{{csrf_field()}}
|
||||
{{ csrf_field() }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2">
|
||||
|
@ -68,12 +69,10 @@
|
|||
{{ Form::label('alert_email', trans('admin/settings/general.alert_email')) }}
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
{{ Form::text('alert_email', old('alert_email', $setting->alert_email), array('class' => 'form-control','placeholder' => 'admin@yourcompany.com')) }}
|
||||
<input type="text" name="alert_email" value="{{ old('alert_email', $setting->alert_email) }}" class="form-control" placeholder="admin@yourcompany.com" maxlength="191">
|
||||
{!! $errors->first('alert_email', '<span class="alert-msg" aria-hidden="true">:message</span><br>') !!}
|
||||
|
||||
<p class="help-block">{{ trans('admin/settings/general.alert_email_help') }}</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -84,7 +83,7 @@
|
|||
{{ Form::label('admin_cc_email', trans('admin/settings/general.admin_cc_email')) }}
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
{{ Form::text('admin_cc_email', old('admin_cc_email', $setting->admin_cc_email), array('class' => 'form-control','placeholder' => 'admin@yourcompany.com')) }}
|
||||
<input type="text" name="admin_cc_email" value="{{ old('admin_cc_email', $setting->admin_cc_email) }}" class="form-control" placeholder="admin@yourcompany.com" maxlength="191">
|
||||
{!! $errors->first('admin_cc_email', '<span class="alert-msg" aria-hidden="true">:message</span><br>') !!}
|
||||
|
||||
<p class="help-block">{{ trans('admin/settings/general.admin_cc_email_help') }}</p>
|
||||
|
@ -122,7 +121,7 @@
|
|||
{{ Form::label('audit_interval', trans('admin/settings/general.audit_interval')) }}
|
||||
</div>
|
||||
<div class="input-group col-md-2">
|
||||
{{ Form::text('audit_interval', old('audit_interval', $setting->audit_interval), array('class' => 'form-control','placeholder' => '12', 'maxlength'=>'3', 'style'=>'width: 60px;')) }}
|
||||
{{ Form::text('audit_interval', old('audit_interval', $setting->audit_interval), array('class' => 'form-control','placeholder' => '12', 'maxlength'=>'3')) }}
|
||||
<span class="input-group-addon">{{ trans('general.months') }}</span>
|
||||
</div>
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
|
@ -137,7 +136,7 @@
|
|||
{{ Form::label('audit_warning_days', trans('admin/settings/general.audit_warning_days')) }}
|
||||
</div>
|
||||
<div class="input-group col-md-2">
|
||||
{{ Form::text('audit_warning_days', old('audit_warning_days', $setting->audit_warning_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3', 'style'=>'width: 60px;')) }}
|
||||
{{ Form::text('audit_warning_days', old('audit_warning_days', $setting->audit_warning_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3')) }}
|
||||
<span class="input-group-addon">{{ trans('general.days') }}</span>
|
||||
</div>
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
|
@ -152,12 +151,8 @@
|
|||
{{ Form::label('due_checkin_days', trans('admin/settings/general.due_checkin_days')) }}
|
||||
</div>
|
||||
<div class="input-group col-md-2">
|
||||
{{ Form::text('due_checkin_days', old('due_checkin_days', $setting->due_checkin_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3', 'style'=>'width: 60px;')) }}
|
||||
{{ Form::text('due_checkin_days', old('due_checkin_days', $setting->due_checkin_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3')) }}
|
||||
<span class="input-group-addon">{{ trans('general.days') }}</span>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
{!! $errors->first('due_checkin_days', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
|
||||
|
||||
{{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'off', 'class' => 'form-horizontal', 'role' => 'form' ]) }}
|
||||
<form method="POST" autocomplete="off" class="form-horizontal" role="form" id="create-form">
|
||||
<!-- CSRF Token -->
|
||||
{{csrf_field()}}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
{{-- Page title --}}
|
||||
@section('title')
|
||||
Update LDAP/AD Settings
|
||||
{{ trans('admin/settings/general.ldap_ad') }}
|
||||
@parent
|
||||
@stop
|
||||
|
||||
|
@ -42,8 +42,7 @@
|
|||
|
||||
@endif
|
||||
|
||||
|
||||
{{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'off', 'class' => 'form-horizontal', 'role' => 'form']) }}
|
||||
<form method="POST" action="{{ route('settings.ldap.save') }}" autocomplete="off" class="form-horizontal" role="form" id="create-form">
|
||||
<!-- CSRF Token -->
|
||||
{{csrf_field()}}
|
||||
|
||||
|
@ -62,7 +61,7 @@
|
|||
<h2 class="box-title">
|
||||
<x-icon type="ldap"/>
|
||||
{{ trans('admin/settings/general.ldap_ad') }}
|
||||
</h4>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
|
||||
|
@ -76,11 +75,15 @@
|
|||
<div class="col-md-8">
|
||||
|
||||
<label class="form-control">
|
||||
{{ Form::checkbox('ldap_enabled', '1', old('ldap_enabled', $setting->ldap_enabled), [((config('app.lock_passwords')===true)) ? 'disabled ': '', 'class' => 'form-control '. $setting->demoMode, $setting->demoMode]) }}
|
||||
{{ Form::checkbox('ldap_enabled', '1', old('ldap_enabled', $setting->ldap_enabled)) }}
|
||||
{{ trans('admin/settings/general.ldap_enabled') }}
|
||||
</label>
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -93,13 +96,21 @@
|
|||
</div>
|
||||
<div class="col-md-8">
|
||||
<label class="form-control">
|
||||
{{ Form::checkbox('is_ad', '1', old('is_ad', $setting->is_ad), [((config('app.lock_passwords')===true)) ? 'disabled ': '', 'class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
|
||||
{{ Form::checkbox('is_ad', '1', old('is_ad', $setting->is_ad)) }}
|
||||
{{ trans('admin/settings/general.is_ad') }}
|
||||
</label>
|
||||
{!! $errors->first('is_ad', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
@error('is_ad')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -111,14 +122,23 @@
|
|||
</div>
|
||||
<div class="col-md-8">
|
||||
<label class="form-control">
|
||||
{{ Form::checkbox('ldap_pw_sync', '1', old('ldap_pw_sync', $setting->ldap_pw_sync), [((config('app.lock_passwords')===true)) ? 'disabled ': '', 'class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
|
||||
{{ Form::checkbox('ldap_pw_sync', '1', old('ldap_pw_sync', $setting->ldap_pw_sync)) }}
|
||||
{{ trans('general.yes') }}
|
||||
</label>
|
||||
|
||||
<p class="help-block">{{ trans('admin/settings/general.ldap_pw_sync_help') }}</p>
|
||||
{!! $errors->first('ldap_pw_sync_help', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
@error('ldap_pw_sync')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
@ -130,42 +150,43 @@
|
|||
{{ Form::label('ad_domain', trans('admin/settings/general.ad_domain')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ad_domain', old('ad_domain', $setting->ad_domain), ['class' => 'form-control','placeholder' => trans('general.example') .'example.com', $setting->demoMode]) }}
|
||||
{{ Form::text('ad_domain', old('ad_domain', $setting->ad_domain), ['class' => 'form-control','placeholder' => trans('general.example') .'example.com']) }}
|
||||
<p class="help-block">{{ trans('admin/settings/general.ad_domain_help') }}</p>
|
||||
{!! $errors->first('ad_domain', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
@error('ad_domain')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div><!-- AD Domain -->
|
||||
|
||||
{{-- NOTICE - this was a feature for AdLdap2-based LDAP syncing, and is already handled in 'classic' LDAP, so we now hide the checkbox (but haven't deleted the field) <!-- AD Append Domain -->
|
||||
<div class="form-group">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('ad_append_domain', trans('admin/settings/general.ad_append_domain_label')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::checkbox('ad_append_domain', '1', old('ad_append_domain', $setting->ad_append_domain),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
|
||||
{{ trans('admin/settings/general.ad_append_domain') }}
|
||||
<p class="help-block">{{ trans('admin/settings/general.ad_append_domain_help') }}</p>
|
||||
{!! $errors->first('ad_append_domain', '<span class="alert-msg">:message</span>') !!}
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div> --}}
|
||||
|
||||
<!-- LDAP Client-Side TLS key -->
|
||||
<div class="form-group {{ $errors->has('ldap_client_tls_key') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('ldap_client_tls_key', trans('admin/settings/general.ldap_client_tls_key')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::textarea('ldap_client_tls_key', old('ldap_client_tls_key', $setting->ldap_client_tls_key), ['class' => 'form-control','placeholder' => trans('general.example') .'-----BEGIN RSA PRIVATE KEY-----'."\r\n1234567890\r\n-----END RSA PRIVATE KEY-----
|
||||
", $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_client_tls_key', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::textarea('ldap_client_tls_key', old('ldap_client_tls_key', $setting->ldap_client_tls_key), ['class' => 'form-control','placeholder' => trans('general.example') .'-----BEGIN RSA PRIVATE KEY-----'."\r\n1234567890\r\n-----END RSA PRIVATE KEY-----"]) }}
|
||||
@error('ldap_client_tls_key')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div><!-- LDAP Client-Side TLS key -->
|
||||
|
@ -176,11 +197,20 @@
|
|||
{{ Form::label('ldap_client_tls_cert', trans('admin/settings/general.ldap_client_tls_cert')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::textarea('ldap_client_tls_cert', old('ldap_client_tls_cert', $setting->ldap_client_tls_cert), ['class' => 'form-control','placeholder' => trans('general.example') .'-----BEGIN CERTIFICATE-----'."\r\n1234567890\r\n-----END CERTIFICATE-----", $setting->demoMode]) }}
|
||||
{{ Form::textarea('ldap_client_tls_cert', old('ldap_client_tls_cert', $setting->ldap_client_tls_cert), ['class' => 'form-control','placeholder' => trans('general.example') .'-----BEGIN CERTIFICATE-----'."\r\n1234567890\r\n-----END CERTIFICATE-----"]) }}
|
||||
<p class="help-block">{{ trans('admin/settings/general.ldap_client_tls_cert_help') }}</p>
|
||||
{!! $errors->first('ldap_client_tls_cert', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
@error('ldap_client_tls_cert')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div><!-- LDAP Client-Side TLS certificate -->
|
||||
|
@ -191,11 +221,21 @@
|
|||
{{ Form::label('ldap_server', trans('admin/settings/general.ldap_server')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_server', old('ldap_server', $setting->ldap_server), ['class' => 'form-control','placeholder' => trans('general.example') .'ldap://ldap.example.com', $setting->demoMode]) }}
|
||||
{{ Form::text('ldap_server', old('ldap_server', $setting->ldap_server), ['class' => 'form-control','placeholder' => trans('general.example') .'ldap://ldap.example.com']) }}
|
||||
@error('ldap_server')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
<p class="help-block">{{ trans('admin/settings/general.ldap_server_help') }}</p>
|
||||
{!! $errors->first('ldap_server', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div><!-- LDAP Server -->
|
||||
|
@ -207,12 +247,21 @@
|
|||
</div>
|
||||
<div class="col-md-8">
|
||||
<label class="form-control">
|
||||
{{ Form::checkbox('ldap_tls', '1', old('ldap_tls', $setting->ldap_tls),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
|
||||
{{ Form::checkbox('ldap_tls', '1', old('ldap_tls', $setting->ldap_tls)) }}
|
||||
{{ trans('admin/settings/general.ldap_tls_help') }}
|
||||
</label>
|
||||
{!! $errors->first('ldap_tls', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
@error('ldap_tls')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -224,13 +273,24 @@
|
|||
</div>
|
||||
<div class="col-md-8">
|
||||
<label class="form-control">
|
||||
{{ Form::checkbox('ldap_server_cert_ignore', '1', old('ldap_server_cert_ignore', $setting->ldap_server_cert_ignore),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
|
||||
{{ Form::checkbox('ldap_server_cert_ignore', '1', old('ldap_server_cert_ignore', $setting->ldap_server_cert_ignore)) }}
|
||||
{{ trans('admin/settings/general.ldap_server_cert_ignore') }}
|
||||
</label>
|
||||
{!! $errors->first('ldap_server_cert_ignore', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<p class="help-block">{{ trans('admin/settings/general.ldap_server_cert_help') }}</p>
|
||||
@error('ldap_server_cert_ignore')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
<p class="help-block">
|
||||
{{ trans('admin/settings/general.ldap_server_cert_help') }}
|
||||
</p>
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -241,10 +301,19 @@
|
|||
{{ Form::label('ldap_uname', trans('admin/settings/general.ldap_uname')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_uname', old('ldap_uname', $setting->ldap_uname), ['class' => 'form-control','autocomplete' => 'off', 'placeholder' => trans('general.example') .'binduser@example.com', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_uname', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::text('ldap_uname', old('ldap_uname', $setting->ldap_uname), ['class' => 'form-control','autocomplete' => 'off', 'placeholder' => trans('general.example') .'binduser@example.com']) }}
|
||||
@error('ldap_uname')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -255,10 +324,19 @@
|
|||
{{ Form::label('ldap_pword', trans('admin/settings/general.ldap_pword')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::password('ldap_pword', ['class' => 'form-control', 'autocomplete' => 'off', 'onfocus' => "this.removeAttribute('readonly');", $setting->demoMode, ' readonly']) }}
|
||||
{!! $errors->first('ldap_pword', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::password('ldap_pword', ['class' => 'form-control', 'autocomplete' => 'off', 'onfocus' => "this.removeAttribute('readonly');", ' readonly']) }}
|
||||
@error('ldap_pword')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -269,10 +347,19 @@
|
|||
{{ Form::label('ldap_basedn', trans('admin/settings/general.ldap_basedn')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_basedn', old('ldap_basedn', $setting->ldap_basedn), ['class' => 'form-control', 'placeholder' => trans('general.example') .'cn=users/authorized,dc=example,dc=com', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_basedn', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::text('ldap_basedn', old('ldap_basedn', $setting->ldap_basedn), ['class' => 'form-control', 'placeholder' => trans('general.example') .'cn=users/authorized,dc=example,dc=com']) }}
|
||||
@error('ldap_basedn')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -283,10 +370,19 @@
|
|||
{{ Form::label('ldap_filter', trans('admin/settings/general.ldap_filter')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_filter', old('ldap_filter', $setting->ldap_filter), ['class' => 'form-control','placeholder' => trans('general.example') .'&(cn=*)', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_filter', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<input type="text" name="ldap_filter" id="ldap_filter" value="{{ old('ldap_filter', $setting->ldap_filter) }}" class="form-control" placeholder="{{ trans('general.example') .'&(cn=*)' }}">
|
||||
@error('ldap_filter')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -297,10 +393,19 @@
|
|||
{{ Form::label('ldap_username_field', trans('admin/settings/general.ldap_username_field')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_username_field', old('ldap_username_field', $setting->ldap_username_field), ['class' => 'form-control','placeholder' => trans('general.example') .'samaccountname', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_username_field', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<input type="text" name="ldap_username_field" id="ldap_username_field" value="{{ old('ldap_username_field', $setting->ldap_username_field) }}" class="form-control" placeholder="{{ trans('general.example') .'samaccountname' }}">
|
||||
@error('ldap_username_field')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{!! $message !!}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -311,10 +416,19 @@
|
|||
{{ Form::label('ldap_lname_field', trans('admin/settings/general.ldap_lname_field')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_lname_field', old('ldap_lname_field', $setting->ldap_lname_field), ['class' => 'form-control','placeholder' => trans('general.example') .'sn', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_lname_field', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<input type="text" name="ldap_lname_field" id="ldap_lname_field" value="{{ old('ldap_lname_field', $setting->ldap_lname_field) }}" class="form-control" placeholder="{{ trans('general.example') .'sn' }}">
|
||||
@error('ldap_lname_field')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -325,10 +439,19 @@
|
|||
{{ Form::label('ldap_fname_field', trans('admin/settings/general.ldap_fname_field')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_fname_field', old('ldap_fname_field', $setting->ldap_fname_field), ['class' => 'form-control', 'placeholder' => trans('general.example') .'givenname', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_fname_field', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
<input type="text" name="ldap_fname_field" id="ldap_fname_field" value="{{ old('ldap_fname_field', $setting->ldap_fname_field) }}" class="form-control" placeholder="{{ trans('general.example') .'givenname' }}">
|
||||
@error('ldap_fname_field')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -336,13 +459,23 @@
|
|||
<!-- LDAP Auth Filter Query -->
|
||||
<div class="form-group {{ $errors->has('ldap_auth_filter_query') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('ldap_auth_filter_query', trans('admin/settings/general.ldap_auth_filter_query')) }}
|
||||
<label for="ldap_auth_filter_query">{{ trans('admin/settings/general.ldap_auth_filter_query') }}</label>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_auth_filter_query', old('ldap_auth_filter_query', $setting->ldap_auth_filter_query), ['class' => 'form-control','placeholder' => trans('general.example') .'uid=', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_auth_filter_query', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
|
||||
<input type="text" name="ldap_auth_filter_query" id="ldap_auth_filter_query" value="{{ old('ldap_auth_filter_query', $setting->ldap_auth_filter_query) }}" class="form-control" placeholder="{{ trans('general.example') .'uid=' }}">
|
||||
@error('ldap_auth_filter_query')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{!! $message !!}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -364,7 +497,6 @@
|
|||
@endforeach
|
||||
</ul>
|
||||
|
||||
|
||||
<span class="help-block">{{ trans('admin/users/general.group_memberships_helpblock') }}</span>
|
||||
@else
|
||||
<div class="controls">
|
||||
|
@ -383,7 +515,7 @@
|
|||
</div>
|
||||
@endif
|
||||
@else
|
||||
<p>No groups have been created yet. Visit <code>Admin Settings > Permission Groups</code> to add one.</p>
|
||||
<p>{!! trans('admin/settings/general.no_groups') !!}</p>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
@ -395,13 +527,21 @@
|
|||
{{ Form::label('ldap_active_flag', trans('admin/settings/general.ldap_active_flag')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_active_flag', old('ldap_active_flag', $setting->ldap_active_flag), ['class' => 'form-control', $setting->demoMode]) }}
|
||||
|
||||
<input type="text" name="ldap_active_flag" id="ldap_active_flag" value="{{ old('ldap_active_flag', $setting->ldap_active_flag) }}" class="form-control">
|
||||
<p class="help-block">{!! trans('admin/settings/general.ldap_activated_flag_help') !!}</p>
|
||||
|
||||
{!! $errors->first('ldap_active_flag', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
@error('ldap_active_flag')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -412,10 +552,19 @@
|
|||
{{ Form::label('ldap_emp_num', trans('admin/settings/general.ldap_emp_num')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_emp_num', old('ldap_emp_num', $setting->ldap_emp_num), ['class' => 'form-control','placeholder' => trans('general.example') .'employeenumber/employeeid', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_emp_num', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::text('ldap_emp_num', old('ldap_emp_num', $setting->ldap_emp_num), ['class' => 'form-control','placeholder' => trans('general.example') .'employeenumber/employeeid']) }}
|
||||
@error('ldap_emp_num')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -425,10 +574,20 @@
|
|||
{{ Form::label('ldap_dept', trans('admin/settings/general.ldap_dept')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_dept', old('ldap_dept', $setting->ldap_dept), ['class' => 'form-control','placeholder' => trans('general.example') .'department', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_dept', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::text('ldap_dept', old('ldap_dept', $setting->ldap_dept), ['class' => 'form-control','placeholder' => trans('general.example') .'department']) }}
|
||||
|
||||
@error('ldap_dept')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -438,10 +597,19 @@
|
|||
{{ Form::label('ldap_dept', trans('admin/settings/general.ldap_manager')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_manager', old('ldap_manager', $setting->ldap_manager), ['class' => 'form-control','placeholder' => trans('general.example') .'manager', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_manager', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::text('ldap_manager', old('ldap_manager', $setting->ldap_manager), ['class' => 'form-control','placeholder' => trans('general.example') .'manager']) }}
|
||||
@error('ldap_manager')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -452,10 +620,19 @@
|
|||
{{ Form::label('ldap_email', trans('admin/settings/general.ldap_email')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_email', old('ldap_email', $setting->ldap_email), ['class' => 'form-control','placeholder' => trans('general.example') .'mail', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_email', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::text('ldap_email', old('ldap_email', $setting->ldap_email), ['class' => 'form-control','placeholder' => trans('general.example') .'mail']) }}
|
||||
@error('ldap_email')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -466,10 +643,19 @@
|
|||
{{ Form::label('ldap_phone', trans('admin/settings/general.ldap_phone')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_phone', old('ldap_phone', $setting->ldap_phone_field), ['class' => 'form-control','placeholder' => trans('general.example') .'telephonenumber', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_phone', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::text('ldap_phone', old('ldap_phone', $setting->ldap_phone_field), ['class' => 'form-control','placeholder' => trans('general.example') .'telephonenumber']) }}
|
||||
@error('ldap_phone')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -480,10 +666,19 @@
|
|||
{{ Form::label('ldap_jobtitle', trans('admin/settings/general.ldap_jobtitle')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_jobtitle', old('ldap_jobtitle', $setting->ldap_jobtitle), ['class' => 'form-control','placeholder' => trans('general.example') .'title', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_jobtitle', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::text('ldap_jobtitle', old('ldap_jobtitle', $setting->ldap_jobtitle), ['class' => 'form-control','placeholder' => trans('general.example') .'title']) }}
|
||||
@error('ldap_jobtitle')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -494,10 +689,19 @@
|
|||
{{ Form::label('ldap_country', trans('admin/settings/general.ldap_country')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_country', old('ldap_country', $setting->ldap_country), ['class' => 'form-control','placeholder' => trans('general.example') .'c', $setting->demoMode]) }}
|
||||
{!! $errors->first('ldap_country', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
{{ Form::text('ldap_country', old('ldap_country', $setting->ldap_country), ['class' => 'form-control','placeholder' => trans('general.example') .'c']) }}
|
||||
@error('ldap_country')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -507,11 +711,20 @@
|
|||
{{ Form::label('ldap_location', trans('admin/settings/general.ldap_location')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('ldap_location', old('ldap_location', $setting->ldap_location), ['class' => 'form-control','placeholder' => trans('general.example') .'physicaldeliveryofficename', $setting->demoMode]) }}
|
||||
{{ Form::text('ldap_location', old('ldap_location', $setting->ldap_location), ['class' => 'form-control','placeholder' => trans('general.example') .'physicaldeliveryofficename']) }}
|
||||
<p class="help-block">{!! trans('admin/settings/general.ldap_location_help') !!}</p>
|
||||
{!! $errors->first('ldap_location', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
@error('ldap_location')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -523,7 +736,7 @@
|
|||
{{ Form::label('test_ldap_sync', 'Test LDAP Sync') }}
|
||||
</div>
|
||||
<div class="col-md-8" id="ldaptestrow">
|
||||
<a {{ $setting->demoMode }} class="btn btn-default btn-sm" id="ldaptest" style="margin-right: 10px;">{{ trans('admin/settings/general.ldap_test_sync') }}</a>
|
||||
<a class="btn btn-default btn-sm" id="ldaptest" style="margin-right: 10px;">{{ trans('admin/settings/general.ldap_test_sync') }}</a>
|
||||
</div>
|
||||
<div class="col-md-8 col-md-offset-3">
|
||||
<br />
|
||||
|
@ -532,7 +745,10 @@
|
|||
<div class="col-md-8 col-md-offset-3">
|
||||
<p class="help-block">{{ trans('admin/settings/general.ldap_login_sync_help') }}</p>
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
@ -578,11 +794,20 @@
|
|||
{{ Form::label('custom_forgot_pass_url', trans('admin/settings/general.custom_forgot_pass_url')) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('custom_forgot_pass_url', old('custom_forgot_pass_url', $setting->custom_forgot_pass_url), ['class' => 'form-control','placeholder' => trans('general.example') .'https://my.ldapserver-forgotpass.com', $setting->demoMode]) }}
|
||||
{{ Form::text('custom_forgot_pass_url', old('custom_forgot_pass_url', $setting->custom_forgot_pass_url), ['class' => 'form-control','placeholder' => trans('general.example') .'https://my.ldapserver-forgotpass.com']) }}
|
||||
<p class="help-block">{{ trans('admin/settings/general.custom_forgot_pass_url_help') }}</p>
|
||||
{!! $errors->first('custom_forgot_pass_url', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
@error('custom_forgot_pass_url')
|
||||
<span class="alert-msg">
|
||||
<x-icon type="x" />
|
||||
{{ $message }}
|
||||
</span>
|
||||
@enderror
|
||||
|
||||
@if (config('app.lock_passwords')===true)
|
||||
<p class="text-warning"><i class="fas fa-lock" aria-hidden="true"></i> {{ trans('general.feature_disabled') }}</p>
|
||||
<p class="text-warning">
|
||||
<x-icon type="locked" />
|
||||
{{ trans('general.feature_disabled') }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div><!-- LDAP Server -->
|
||||
|
@ -607,9 +832,10 @@
|
|||
{{Form::close()}}
|
||||
|
||||
|
||||
@stop
|
||||
@endsection
|
||||
|
||||
@push('js')
|
||||
|
||||
<script nonce="{{ csrf_token() }}">
|
||||
|
||||
|
||||
|
@ -618,11 +844,58 @@
|
|||
* Check to see if is_ad is checked, if not disable the ad_domain field
|
||||
*/
|
||||
$(function() {
|
||||
if( $('#is_ad').prop('checked') === false) {
|
||||
|
||||
// If the app is locked, disable all fields except the top search fields
|
||||
@if (config('app.lock_passwords') === true)
|
||||
$("input").prop('disabled', 'disabled');
|
||||
$("textarea").prop('disabled', 'disabled');
|
||||
$("button").prop('disabled', 'disabled');
|
||||
$("#tagSearch").removeAttr("disabled");
|
||||
$("#search").removeAttr("disabled");
|
||||
$("#topSearchButton").removeAttr("disabled");
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
if ($('#is_ad').prop('checked') === false) {
|
||||
$('#ad_domain').prop('disabled', 'disabled');
|
||||
} else {
|
||||
//$('#ldap_server').prop('disabled', 'disabled');
|
||||
$("#ad_domain").prop('required',false);
|
||||
}
|
||||
|
||||
|
||||
// Mark fields as required if LDAP is enabled
|
||||
if ($('#ldap_enabled').prop('checked') === false) {
|
||||
$("#ldap_server").prop('required',false);
|
||||
$("#ldap_auth_filter_query").prop('required',false);
|
||||
$("#ldap_filter").prop('required',false);
|
||||
$("#ldap_username_field").prop('required',false);
|
||||
$("#ldap_uname").prop('required',false);
|
||||
$("#ldap_pword").prop('required',false);
|
||||
$("#ldap_basedn").prop('required',false);
|
||||
$("#ldap_fname_field").prop('required',false);
|
||||
}
|
||||
|
||||
$("#ldap_enabled").change(function() {
|
||||
|
||||
if (this.checked) {
|
||||
$("#ldap_server").prop('required',true);
|
||||
$("#ldap_auth_filter_query").prop('required',true);
|
||||
$("#ldap_filter").prop('required',true);
|
||||
$("#ldap_uname").prop('required',true);
|
||||
$("#ldap_username_field").prop('required',true);
|
||||
$("#ldap_pword").prop('required',true);
|
||||
$("#ldap_basedn").prop('required',true);
|
||||
} else {
|
||||
$("#ldap_server").prop('required',false);
|
||||
$("#ldap_auth_filter_query").prop('required',false);
|
||||
$("#ldap_filter").prop('required',false);
|
||||
$("#ldap_username_field").prop('required',false);
|
||||
$("#ldap_pword").prop('required',false);
|
||||
$("#ldap_basedn").prop('required',false);
|
||||
$("#ldap_fname_field").prop('required',false);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
$("#is_ad").change(function() {
|
||||
|
@ -649,7 +922,7 @@
|
|||
$("#ldaptest").click(function () {
|
||||
$("#ldapad_test_results").removeClass('hidden text-success text-danger');
|
||||
$("#ldapad_test_results").html('');
|
||||
$("#ldapad_test_results").html('<i class="fas fa-spinner spin"></i> {{ trans('admin/settings/message.ldap.testing') }}');
|
||||
$("#ldapad_test_results").html('<x-icon type="spinner" /> {{ trans('admin/settings/message.ldap.testing') }}');
|
||||
$.ajax({
|
||||
url: '{{ route('api.settings.ldaptest') }}',
|
||||
type: 'GET',
|
||||
|
@ -698,8 +971,8 @@
|
|||
*/
|
||||
function buildLdapTestResults(results) {
|
||||
let html = '<ul style="list-style: none;padding-left: 5px;">'
|
||||
html += '<li class="text-success"><i class="fas fa-check" aria-hidden="true"></i> ' + results.login.message + ' </li>'
|
||||
html += '<li class="text-success"><i class="fas fa-check" aria-hidden="true"></i> ' + results.bind.message + ' </li>'
|
||||
html += '<li class="text-success"><i class="fas fa-check""></i> ' + results.login.message + ' </li>'
|
||||
html += '<li class="text-success"><i class="fas fa-check""></i> ' + results.bind.message + ' </li>'
|
||||
html += '</ul>'
|
||||
html += '<div style="overflow:auto;">'
|
||||
html += '<div>{{ trans('admin/settings/message.ldap.sync_success') }}</div>'
|
||||
|
@ -738,12 +1011,13 @@
|
|||
return body;
|
||||
}
|
||||
|
||||
|
||||
$("#ldaptestlogin").click(function(){
|
||||
$("#ldaptestloginrow").removeClass('text-success');
|
||||
$("#ldaptestloginrow").removeClass('text-danger');
|
||||
$("#ldaptestloginstatus").removeClass('text-danger');
|
||||
$("#ldaptestloginstatus").html('');
|
||||
$("#ldaptestloginicon").html('<i class="fas fa-spinner spin"></i> {{ trans('admin/settings/message.ldap.testing_authentication') }}');
|
||||
$("#ldaptestloginicon").html('<x-icon type="spinner" /> {{ trans('admin/settings/message.ldap.testing_authentication') }}');
|
||||
$.ajax({
|
||||
url: '{{ route('api.settings.ldaptestlogin') }}',
|
||||
type: 'POST',
|
||||
|
@ -803,9 +1077,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
|
||||
|
||||
|
||||
{{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'off', 'class' => 'form-horizontal', 'role' => 'form' ]) }}
|
||||
<form method="POST" autocomplete="off" class="form-horizontal" role="form" id="create-form">
|
||||
|
||||
<!-- CSRF Token -->
|
||||
{{csrf_field()}}
|
||||
{{ csrf_field() }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
@if (count($users) === 1)
|
||||
@if ((isset($users) && count($users) === 1))
|
||||
<title>{{ trans('general.assigned_to', ['name' => $users[0]->present()->fullName()]) }} - {{ date('Y-m-d H:i', time()) }}</title>
|
||||
@else
|
||||
<title>{{ trans('admin/users/general.print_assigned') }} - {{ date('Y-m-d H:i', time()) }}</title>
|
||||
|
@ -36,11 +36,7 @@
|
|||
@page {
|
||||
size: A4;
|
||||
}
|
||||
|
||||
#start_of_user_section {
|
||||
break-before: page;
|
||||
}
|
||||
|
||||
|
||||
.print-logo {
|
||||
max-height: 40px;
|
||||
}
|
||||
|
@ -51,13 +47,6 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<script nonce="{{ csrf_token() }}">
|
||||
window.snipeit = {
|
||||
settings: {
|
||||
"per_page": 50
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
@ -384,8 +373,11 @@
|
|||
</table>
|
||||
@endif
|
||||
|
||||
@php
|
||||
if (!empty($eulas)) $eulas = array_unique($eulas);
|
||||
@endphp
|
||||
{{-- This may have been render at the top of the page if we're rendering more than one user... --}}
|
||||
@if (count($users) === 1)
|
||||
@if (count($users) === 1 && !empty($eulas))
|
||||
<p></p>
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-default hidden-print" type="button" data-toggle="collapse" data-target=".eula-row" aria-expanded="false" aria-controls="eula-row" title="EULAs">
|
||||
|
@ -395,19 +387,16 @@
|
|||
@endif
|
||||
|
||||
<table style="margin-top: 80px;">
|
||||
@if (!empty($eulas))
|
||||
<tr class="collapse eula-row">
|
||||
<td style="padding-right: 10px; vertical-align: top; font-weight: bold;">EULA</td>
|
||||
<td style="padding-right: 10px; vertical-align: top; padding-bottom: 80px;" colspan="3">
|
||||
@php
|
||||
if (!empty($eulas)) $eulas = array_unique($eulas);
|
||||
@endphp
|
||||
@if (!empty($eulas))
|
||||
@foreach ($eulas as $key => $eula)
|
||||
{!! $eula !!}
|
||||
@endforeach
|
||||
@endif
|
||||
@foreach ($eulas as $key => $eula)
|
||||
{!! $eula !!}
|
||||
@endforeach
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
<tr>
|
||||
<td style="padding-right: 10px; vertical-align: top; font-weight: bold;">{{ trans('general.signed_off_by') }}:</td>
|
||||
<td style="padding-right: 10px; vertical-align: top;">______________________________________</td>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<x-icon type="assets" class="fa-2x" />
|
||||
</span>
|
||||
<span class="hidden-xs hidden-sm">{{ trans('general.assets') }}
|
||||
{!! ($user->assets()->AssetsForShow()->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($user->assets()->AssetsForShow()->count()).'</badge>' : '' !!}
|
||||
{!! ($user->assets()->AssetsForShow()->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($user->assets()->AssetsForShow()->withoutTrashed()->count()).'</badge>' : '' !!}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -177,8 +177,6 @@
|
|||
<div class="col-md-12 text-center">
|
||||
<img src="{{ $user->present()->gravatar() }}" class=" img-thumbnail hidden-print" style="margin-bottom: 20px;" alt="{{ $user->present()->fullName() }}">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@can('update', $user)
|
||||
<div class="col-md-12">
|
||||
|
|
|
@ -536,13 +536,16 @@ Route::group(['middleware' => 'web'], function () {
|
|||
)->name('logout.post');
|
||||
});
|
||||
|
||||
//Auth::routes();
|
||||
|
||||
Route::get(
|
||||
'/health',
|
||||
/**
|
||||
* Health check route - skip middleware
|
||||
*/
|
||||
Route::withoutMiddleware(['web'])->get(
|
||||
'/health',
|
||||
[HealthController::class, 'get']
|
||||
)->name('health');
|
||||
|
||||
|
||||
Route::middleware(['auth'])->get(
|
||||
'/',
|
||||
[DashboardController::class, 'index']
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Accessories\Api;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Tests\Concerns\TestsFullMultipleCompaniesSupport;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AccessoriesForSelectListTest extends TestCase implements TestsFullMultipleCompaniesSupport
|
||||
{
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping()
|
||||
{
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
|
||||
$accessoryA = Accessory::factory()->for($companyA)->create();
|
||||
$accessoryB = Accessory::factory()->for($companyB)->create();
|
||||
|
||||
$superuser = User::factory()->superuser()->create();
|
||||
$userInCompanyA = $companyA->users()->save(User::factory()->viewAccessories()->make());
|
||||
$userInCompanyB = $companyB->users()->save(User::factory()->viewAccessories()->make());
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$this->actingAsForApi($userInCompanyA)
|
||||
->getJson(route('api.accessories.selectlist'))
|
||||
->assertOk()
|
||||
->assertJsonPath('total_count', 1)
|
||||
->assertResponseContainsInResults($accessoryA)
|
||||
->assertResponseDoesNotContainInResults($accessoryB);
|
||||
|
||||
$this->actingAsForApi($userInCompanyB)
|
||||
->getJson(route('api.accessories.selectlist'))
|
||||
->assertOk()
|
||||
->assertJsonPath('total_count', 1)
|
||||
->assertResponseDoesNotContainInResults($accessoryA)
|
||||
->assertResponseContainsInResults($accessoryB);
|
||||
|
||||
$this->actingAsForApi($superuser)
|
||||
->getJson(route('api.accessories.selectlist'))
|
||||
->assertOk()
|
||||
->assertJsonPath('total_count', 2)
|
||||
->assertResponseContainsInResults($accessoryA)
|
||||
->assertResponseContainsInResults($accessoryB);
|
||||
}
|
||||
|
||||
public function testCanGetAccessoriesForSelectList()
|
||||
{
|
||||
[$accessoryA, $accessoryB] = Accessory::factory()->count(2)->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->viewAccessories()->create())
|
||||
->getJson(route('api.accessories.selectlist'))
|
||||
->assertOk()
|
||||
->assertJsonPath('total_count', 2)
|
||||
->assertResponseContainsInResults($accessoryA)
|
||||
->assertResponseContainsInResults($accessoryB);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Accessories\Api;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Tests\Concerns\TestsFullMultipleCompaniesSupport;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\TestCase;
|
||||
|
||||
class IndexAccessoryCheckoutsTest extends TestCase implements TestsFullMultipleCompaniesSupport, TestsPermissionsRequirement
|
||||
{
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$accessory = Accessory::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->create())
|
||||
->getJson(route('api.accessories.checkedout', $accessory))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping()
|
||||
{
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
|
||||
$accessoryA = Accessory::factory()->for($companyA)->create();
|
||||
$accessoryB = Accessory::factory()->for($companyB)->create();
|
||||
|
||||
$superuser = User::factory()->superuser()->create();
|
||||
$userInCompanyA = $companyA->users()->save(User::factory()->viewAccessories()->make());
|
||||
$userInCompanyB = $companyB->users()->save(User::factory()->viewAccessories()->make());
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$this->actingAsForApi($userInCompanyA)
|
||||
->getJson(route('api.accessories.checkedout', $accessoryB))
|
||||
->assertStatusMessageIs('error');
|
||||
|
||||
$this->actingAsForApi($userInCompanyB)
|
||||
->getJson(route('api.accessories.checkedout', $accessoryA))
|
||||
->assertStatusMessageIs('error');
|
||||
|
||||
$this->actingAsForApi($superuser)
|
||||
->getJson(route('api.accessories.checkedout', $accessoryA))
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
public function testCanGetAccessoryCheckouts()
|
||||
{
|
||||
[$userA, $userB] = User::factory()->count(2)->create();
|
||||
|
||||
$accessory = Accessory::factory()->checkedOutToUsers([$userA, $userB])->create();
|
||||
|
||||
$this->assertEquals(2, $accessory->checkouts()->count());
|
||||
|
||||
$this->actingAsForApi(User::factory()->viewAccessories()->create())
|
||||
->getJson(route('api.accessories.checkedout', $accessory))
|
||||
->assertOk()
|
||||
->assertJsonPath('total', 2)
|
||||
->assertJsonPath('rows.0.assigned_to.id', $userA->id)
|
||||
->assertJsonPath('rows.1.assigned_to.id', $userB->id);
|
||||
}
|
||||
|
||||
public function testCanGetAccessoryCheckoutsWithOffsetAndLimitInQueryString()
|
||||
{
|
||||
[$userA, $userB, $userC] = User::factory()->count(3)->create();
|
||||
|
||||
$accessory = Accessory::factory()->checkedOutToUsers([$userA, $userB, $userC])->create();
|
||||
|
||||
$actor = $this->actingAsForApi(User::factory()->viewAccessories()->create());
|
||||
|
||||
$actor->getJson(route('api.accessories.checkedout', ['accessory' => $accessory->id, 'limit' => 1]))
|
||||
->assertOk()
|
||||
->assertJsonPath('total', 3)
|
||||
->assertJsonPath('rows.0.assigned_to.id', $userA->id);
|
||||
|
||||
$actor->getJson(route('api.accessories.checkedout', ['accessory' => $accessory->id, 'limit' => 2, 'offset' => 1]))
|
||||
->assertOk()
|
||||
->assertJsonPath('total', 3)
|
||||
->assertJsonPath('rows.0.assigned_to.id', $userB->id)
|
||||
->assertJsonPath('rows.1.assigned_to.id', $userC->id);
|
||||
}
|
||||
}
|
|
@ -2,15 +2,69 @@
|
|||
|
||||
namespace Tests\Feature\Accessories\Api;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Tests\Concerns\TestsFullMultipleCompaniesSupport;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\TestCase;
|
||||
|
||||
class IndexAccessoryTest extends TestCase
|
||||
class IndexAccessoryTest extends TestCase implements TestsFullMultipleCompaniesSupport, TestsPermissionsRequirement
|
||||
{
|
||||
public function testPermissionRequiredToViewAccessoriesIndex()
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->create())
|
||||
->getJson(route('api.accessories.index'))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping()
|
||||
{
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
|
||||
$accessoryA = Accessory::factory()->for($companyA)->create(['name' => 'Accessory A']);
|
||||
$accessoryB = Accessory::factory()->for($companyB)->create(['name' => 'Accessory B']);
|
||||
$accessoryC = Accessory::factory()->for($companyB)->create(['name' => 'Accessory C']);
|
||||
|
||||
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||
$userInCompanyA = $companyA->users()->save(User::factory()->viewAccessories()->make());
|
||||
$userInCompanyB = $companyB->users()->save(User::factory()->viewAccessories()->make());
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$this->actingAsForApi($userInCompanyA)
|
||||
->getJson(route('api.accessories.index'))
|
||||
->assertOk()
|
||||
->assertResponseContainsInRows($accessoryA)
|
||||
->assertResponseDoesNotContainInRows($accessoryB)
|
||||
->assertResponseDoesNotContainInRows($accessoryC);
|
||||
|
||||
$this->actingAsForApi($userInCompanyB)
|
||||
->getJson(route('api.accessories.index'))
|
||||
->assertOk()
|
||||
->assertResponseDoesNotContainInRows($accessoryA)
|
||||
->assertResponseContainsInRows($accessoryB)
|
||||
->assertResponseContainsInRows($accessoryC);
|
||||
|
||||
$this->actingAsForApi($superUser)
|
||||
->getJson(route('api.accessories.index'))
|
||||
->assertOk()
|
||||
->assertResponseContainsInRows($accessoryA)
|
||||
->assertResponseContainsInRows($accessoryB)
|
||||
->assertResponseContainsInRows($accessoryC);
|
||||
}
|
||||
|
||||
public function testCanGetAccessories()
|
||||
{
|
||||
$user = User::factory()->viewAccessories()->create();
|
||||
|
||||
$accessoryA = Accessory::factory()->create(['name' => 'Accessory A']);
|
||||
$accessoryB = Accessory::factory()->create(['name' => 'Accessory B']);
|
||||
|
||||
$this->actingAsForApi($user)
|
||||
->getJson(route('api.accessories.index'))
|
||||
->assertOk()
|
||||
->assertResponseContainsInRows($accessoryA)
|
||||
->assertResponseContainsInRows($accessoryB);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
namespace Tests\Feature\Accessories\Api;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Tests\Concerns\TestsFullMultipleCompaniesSupport;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ShowAccessoryTest extends TestCase
|
||||
class ShowAccessoryTest extends TestCase implements TestsFullMultipleCompaniesSupport, TestsPermissionsRequirement
|
||||
{
|
||||
public function testPermissionRequiredToShowAccessory()
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$accessory = Accessory::factory()->create();
|
||||
|
||||
|
@ -16,4 +19,43 @@ class ShowAccessoryTest extends TestCase
|
|||
->getJson(route('api.accessories.show', $accessory))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping()
|
||||
{
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
|
||||
$accessoryForCompanyA = Accessory::factory()->for($companyA)->create();
|
||||
|
||||
$superuser = User::factory()->superuser()->create();
|
||||
$userForCompanyB = User::factory()->for($companyB)->viewAccessories()->create();
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$this->actingAsForApi($userForCompanyB)
|
||||
->getJson(route('api.accessories.show', $accessoryForCompanyA))
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error');
|
||||
|
||||
$this->actingAsForApi($superuser)
|
||||
->getJson(route('api.accessories.show', $accessoryForCompanyA))
|
||||
->assertOk()
|
||||
->assertJsonFragment([
|
||||
'id' => $accessoryForCompanyA->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCanGetSingleAccessory()
|
||||
{
|
||||
$accessory = Accessory::factory()->checkedOutToUser()->create(['name' => 'My Accessory']);
|
||||
|
||||
$this->actingAsForApi(User::factory()->viewAccessories()->create())
|
||||
->getJson(route('api.accessories.show', $accessory))
|
||||
->assertOk()
|
||||
->assertJsonFragment([
|
||||
'id' => $accessory->id,
|
||||
'name' => 'My Accessory',
|
||||
'checkouts_count' => 1,
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,97 @@
|
|||
|
||||
namespace Tests\Feature\Accessories\Api;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use Tests\Concerns\TestsFullMultipleCompaniesSupport;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\TestCase;
|
||||
|
||||
class StoreAccessoryTest extends TestCase
|
||||
class StoreAccessoryTest extends TestCase implements TestsFullMultipleCompaniesSupport, TestsPermissionsRequirement
|
||||
{
|
||||
public function testPermissionRequiredToStoreAccessory()
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->create())
|
||||
->postJson(route('api.accessories.store'))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping()
|
||||
{
|
||||
$this->markTestSkipped('This behavior is not implemented');
|
||||
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
$userInCompanyA = User::factory()->for($companyA)->createAccessories()->create();
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
// attempt to store an accessory for company B
|
||||
$this->actingAsForApi($userInCompanyA)
|
||||
->postJson(route('api.accessories.store'), [
|
||||
'category_id' => Category::factory()->forAccessories()->create()->id,
|
||||
'name' => 'My Awesome Accessory',
|
||||
'qty' => 1,
|
||||
'company_id' => $companyB->id,
|
||||
])->assertStatusMessageIs('error');
|
||||
|
||||
$this->assertDatabaseMissing('accessories', [
|
||||
'name' => 'My Awesome Accessory',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testValidation()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->createAccessories()->create())
|
||||
->postJson(route('api.accessories.store'), [
|
||||
//
|
||||
])
|
||||
->assertStatusMessageIs('error')
|
||||
->assertMessagesContains([
|
||||
'category_id',
|
||||
'name',
|
||||
'qty',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCanStoreAccessory()
|
||||
{
|
||||
$category = Category::factory()->forAccessories()->create();
|
||||
$company = Company::factory()->create();
|
||||
$location = Location::factory()->create();
|
||||
$manufacturer = Manufacturer::factory()->create();
|
||||
$supplier = Supplier::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->createAccessories()->create())
|
||||
->postJson(route('api.accessories.store'), [
|
||||
'name' => 'My Awesome Accessory',
|
||||
'qty' => 2,
|
||||
'order_number' => '12345',
|
||||
'purchase_cost' => 100.00,
|
||||
'purchase_date' => '2024-09-18',
|
||||
'model_number' => '98765',
|
||||
'category_id' => $category->id,
|
||||
'company_id' => $company->id,
|
||||
'location_id' => $location->id,
|
||||
'manufacturer_id' => $manufacturer->id,
|
||||
'supplier_id' => $supplier->id,
|
||||
])->assertStatusMessageIs('success');
|
||||
|
||||
$this->assertDatabaseHas('accessories', [
|
||||
'name' => 'My Awesome Accessory',
|
||||
'qty' => 2,
|
||||
'order_number' => '12345',
|
||||
'purchase_cost' => 100.00,
|
||||
'purchase_date' => '2024-09-18',
|
||||
'model_number' => '98765',
|
||||
'category_id' => $category->id,
|
||||
'company_id' => $company->id,
|
||||
'location_id' => $location->id,
|
||||
'manufacturer_id' => $manufacturer->id,
|
||||
'supplier_id' => $supplier->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,19 @@
|
|||
namespace Tests\Feature\Accessories\Api;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use Tests\Concerns\TestsFullMultipleCompaniesSupport;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UpdateAccessoryTest extends TestCase
|
||||
class UpdateAccessoryTest extends TestCase implements TestsFullMultipleCompaniesSupport, TestsPermissionsRequirement
|
||||
{
|
||||
public function testPermissionRequiredToUpdateAccessory()
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$accessory = Accessory::factory()->create();
|
||||
|
||||
|
@ -16,4 +23,84 @@ class UpdateAccessoryTest extends TestCase
|
|||
->patchJson(route('api.accessories.update', $accessory))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping()
|
||||
{
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
|
||||
$accessoryA = Accessory::factory()->for($companyA)->create(['name' => 'A Name to Change']);
|
||||
$accessoryB = Accessory::factory()->for($companyB)->create(['name' => 'A Name to Change']);
|
||||
$accessoryC = Accessory::factory()->for($companyB)->create(['name' => 'A Name to Change']);
|
||||
|
||||
$superuser = User::factory()->superuser()->create();
|
||||
$userInCompanyA = $companyA->users()->save(User::factory()->editAccessories()->make());
|
||||
$userInCompanyB = $companyB->users()->save(User::factory()->editAccessories()->make());
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$this->actingAsForApi($userInCompanyA)
|
||||
->patchJson(route('api.accessories.update', $accessoryB), ['name' => 'New Name'])
|
||||
->assertStatusMessageIs('error');
|
||||
|
||||
$this->actingAsForApi($userInCompanyB)
|
||||
->patchJson(route('api.accessories.update', $accessoryA), ['name' => 'New Name'])
|
||||
->assertStatusMessageIs('error');
|
||||
|
||||
$this->actingAsForApi($superuser)
|
||||
->patchJson(route('api.accessories.update', $accessoryC), ['name' => 'New Name'])
|
||||
->assertOk();
|
||||
|
||||
$this->assertEquals('A Name to Change', $accessoryA->fresh()->name);
|
||||
$this->assertEquals('A Name to Change', $accessoryB->fresh()->name);
|
||||
$this->assertEquals('New Name', $accessoryC->fresh()->name);
|
||||
}
|
||||
|
||||
public function testCanUpdateAccessoryViaPatch()
|
||||
{
|
||||
[$categoryA, $categoryB] = Category::factory()->count(2)->create();
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
[$locationA, $locationB] = Location::factory()->count(2)->create();
|
||||
[$manufacturerA, $manufacturerB] = Manufacturer::factory()->count(2)->create();
|
||||
[$supplierA, $supplierB] = Supplier::factory()->count(2)->create();
|
||||
|
||||
$accessory = Accessory::factory()->create([
|
||||
'name' => 'A Name to Change',
|
||||
'qty' => 5,
|
||||
'order_number' => 'A12345',
|
||||
'purchase_cost' => 99.99,
|
||||
'model_number' => 'ABC098',
|
||||
'category_id' => $categoryA->id,
|
||||
'company_id' => $companyA->id,
|
||||
'location_id' => $locationA->id,
|
||||
'manufacturer_id' => $manufacturerA->id,
|
||||
'supplier_id' => $supplierA->id,
|
||||
]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->editAccessories()->create())
|
||||
->patchJson(route('api.accessories.update', $accessory), [
|
||||
'name' => 'A New Name',
|
||||
'qty' => 10,
|
||||
'order_number' => 'B54321',
|
||||
'purchase_cost' => 199.99,
|
||||
'model_number' => 'XYZ123',
|
||||
'category_id' => $categoryB->id,
|
||||
'company_id' => $companyB->id,
|
||||
'location_id' => $locationB->id,
|
||||
'manufacturer_id' => $manufacturerB->id,
|
||||
'supplier_id' => $supplierB->id,
|
||||
])
|
||||
->assertOk();
|
||||
|
||||
$accessory = $accessory->fresh();
|
||||
$this->assertEquals('A New Name', $accessory->name);
|
||||
$this->assertEquals(10, $accessory->qty);
|
||||
$this->assertEquals('B54321', $accessory->order_number);
|
||||
$this->assertEquals(199.99, $accessory->purchase_cost);
|
||||
$this->assertEquals('XYZ123', $accessory->model_number);
|
||||
$this->assertEquals($categoryB->id, $accessory->category_id);
|
||||
$this->assertEquals($companyB->id, $accessory->company_id);
|
||||
$this->assertEquals($locationB->id, $accessory->location_id);
|
||||
$this->assertEquals($manufacturerB->id, $accessory->manufacturer_id);
|
||||
$this->assertEquals($supplierB->id, $accessory->supplier_id);
|
||||
}
|
||||
}
|
||||
|
|
86
tests/Feature/Checkins/Api/AccessoryCheckinTest.php
Normal file
86
tests/Feature/Checkins/Api/AccessoryCheckinTest.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Checkins\Api;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Tests\Concerns\TestsFullMultipleCompaniesSupport;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AccessoryCheckinTest extends TestCase implements TestsFullMultipleCompaniesSupport, TestsPermissionsRequirement
|
||||
{
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$accessory = Accessory::factory()->checkedOutToUser()->create();
|
||||
$accessoryCheckout = $accessory->checkouts->first();
|
||||
|
||||
$this->actingAsForApi(User::factory()->create())
|
||||
->postJson(route('api.accessories.checkin', $accessoryCheckout))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping()
|
||||
{
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
|
||||
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||
$userInCompanyA = User::factory()->for($companyA)->checkinAccessories()->create();
|
||||
$accessoryForCompanyB = Accessory::factory()->for($companyB)->checkedOutToUser()->create();
|
||||
$anotherAccessoryForCompanyB = Accessory::factory()->for($companyB)->checkedOutToUser()->create();
|
||||
|
||||
$this->assertEquals(1, $accessoryForCompanyB->checkouts->count());
|
||||
$this->assertEquals(1, $anotherAccessoryForCompanyB->checkouts->count());
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$this->actingAsForApi($userInCompanyA)
|
||||
->postJson(route('api.accessories.checkin', $accessoryForCompanyB->checkouts->first()))
|
||||
->assertForbidden();
|
||||
|
||||
$this->actingAsForApi($superUser)
|
||||
->postJson(route('api.accessories.checkin', $anotherAccessoryForCompanyB->checkouts->first()))
|
||||
->assertStatusMessageIs('success');
|
||||
|
||||
$this->assertEquals(1, $accessoryForCompanyB->fresh()->checkouts->count(), 'Accessory should not be checked in');
|
||||
$this->assertEquals(0, $anotherAccessoryForCompanyB->fresh()->checkouts->count(), 'Accessory should be checked in');
|
||||
}
|
||||
|
||||
public function testCanCheckinAccessory()
|
||||
{
|
||||
$accessory = Accessory::factory()->checkedOutToUser()->create();
|
||||
|
||||
$this->assertEquals(1, $accessory->checkouts->count());
|
||||
|
||||
$accessoryCheckout = $accessory->checkouts->first();
|
||||
|
||||
$this->actingAsForApi(User::factory()->checkinAccessories()->create())
|
||||
->postJson(route('api.accessories.checkin', $accessoryCheckout))
|
||||
->assertStatusMessageIs('success');
|
||||
|
||||
$this->assertEquals(0, $accessory->fresh()->checkouts->count(), 'Accessory should be checked in');
|
||||
}
|
||||
|
||||
public function testCheckinIsLogged()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$actor = User::factory()->checkinAccessories()->create();
|
||||
|
||||
$accessory = Accessory::factory()->checkedOutToUser($user)->create();
|
||||
$accessoryCheckout = $accessory->checkouts->first();
|
||||
|
||||
$this->actingAsForApi($actor)
|
||||
->postJson(route('api.accessories.checkin', $accessoryCheckout))
|
||||
->assertStatusMessageIs('success');
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'created_by' => $actor->id,
|
||||
'action_type' => 'checkin from',
|
||||
'target_id' => $user->id,
|
||||
'target_type' => User::class,
|
||||
'item_id' => $accessory->id,
|
||||
'item_type' => Accessory::class,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -7,11 +7,12 @@ use App\Models\Actionlog;
|
|||
use App\Models\User;
|
||||
use App\Notifications\CheckoutAccessoryNotification;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AccessoryCheckoutTest extends TestCase
|
||||
class AccessoryCheckoutTest extends TestCase implements TestsPermissionsRequirement
|
||||
{
|
||||
public function testCheckingOutAccessoryRequiresCorrectPermission()
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->create())
|
||||
->postJson(route('api.accessories.checkout', Accessory::factory()->create()))
|
||||
|
|
16
tests/Feature/Importing/Api/GeneralImportTest.php
Normal file
16
tests/Feature/Importing/Api/GeneralImportTest.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Importing\Api;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
class GeneralImportTest extends ImportDataTestCase
|
||||
{
|
||||
public function testRequiresExistingImport()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->canImport()->create());
|
||||
|
||||
$this->importFileResponse(['import' => 9999, 'import-type' => 'accessory'])
|
||||
->assertStatusMessageIs('import-errors');
|
||||
}
|
||||
}
|
420
tests/Feature/Importing/Api/ImportAccessoriesTest.php
Normal file
420
tests/Feature/Importing/Api/ImportAccessoriesTest.php
Normal file
|
@ -0,0 +1,420 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Importing\Api;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Import;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Str;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\Support\Importing\AccessoriesImportFileBuilder as ImportFileBuilder;
|
||||
use Tests\Support\Importing\CleansUpImportFiles;
|
||||
|
||||
class ImportAccessoriesTest extends ImportDataTestCase implements TestsPermissionsRequirement
|
||||
{
|
||||
use CleansUpImportFiles;
|
||||
use WithFaker;
|
||||
|
||||
protected function importFileResponse(array $parameters = []): TestResponse
|
||||
{
|
||||
if (!array_key_exists('import-type', $parameters)) {
|
||||
$parameters['import-type'] = 'accessory';
|
||||
}
|
||||
|
||||
return parent::importFileResponse($parameters);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->create());
|
||||
|
||||
$this->importFileResponse(['import' => 44])->assertForbidden();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function userWithImportAccessoryPermissionCanImportAccessories(): void
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->canImport()->create());
|
||||
|
||||
$import = Import::factory()->accessory()->create();
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function importAccessory(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new();
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertOk()
|
||||
->assertExactJson([
|
||||
'payload' => null,
|
||||
'status' => 'success',
|
||||
'messages' => [
|
||||
'redirect_url' => route('accessories.index')
|
||||
]
|
||||
]);
|
||||
|
||||
$newAccessory = Accessory::query()
|
||||
->with(['location', 'category', 'manufacturer', 'supplier', 'company'])
|
||||
->where('name', $row['itemName'])
|
||||
->sole();
|
||||
|
||||
$activityLog = Actionlog::query()
|
||||
->where('item_type', Accessory::class)
|
||||
->where('item_id', $newAccessory->id)
|
||||
->sole();
|
||||
|
||||
$this->assertEquals('create', $activityLog->action_type);
|
||||
$this->assertEquals('importer', $activityLog->action_source);
|
||||
$this->assertEquals($newAccessory->company->id, $activityLog->company_id);
|
||||
|
||||
$this->assertEquals($row['itemName'], $newAccessory->name);
|
||||
$this->assertEquals($row['quantity'], $newAccessory->qty);
|
||||
$this->assertEquals($row['purchaseDate'], $newAccessory->purchase_date->toDateString());
|
||||
$this->assertEquals($row['purchaseCost'], $newAccessory->purchase_cost);
|
||||
$this->assertEquals($row['orderNumber'], $newAccessory->order_number);
|
||||
$this->assertEquals($row['notes'], $newAccessory->notes);
|
||||
$this->assertEquals($row['category'], $newAccessory->category->name);
|
||||
$this->assertEquals('accessory', $newAccessory->category->category_type);
|
||||
$this->assertEquals($row['manufacturerName'], $newAccessory->manufacturer->name);
|
||||
$this->assertEquals($row['supplierName'], $newAccessory->supplier->name);
|
||||
$this->assertEquals($row['location'], $newAccessory->location->name);
|
||||
$this->assertEquals($row['companyName'], $newAccessory->company->name);
|
||||
$this->assertEquals($row['modelNumber'], $newAccessory->model_number);
|
||||
$this->assertFalse($newAccessory->requestable);
|
||||
$this->assertNull($newAccessory->min_amt);
|
||||
$this->assertNull($newAccessory->user_id);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function whenImportFileContainsUnknownColumns(): void
|
||||
{
|
||||
$row = ImportFileBuilder::new()->definition();
|
||||
$row['unknownColumn'] = $this->faker->word;
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willFormatDate(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new(['purchaseDate' => '2022/10/10']);
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$accessory = Accessory::query()
|
||||
->where('name', $importFileBuilder->firstRow()['itemName'])
|
||||
->sole(['purchase_date']);
|
||||
|
||||
$this->assertEquals('2022-10-10', $accessory->purchase_date->toDateString());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewCategoryWhenCategoryExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['category' => Str::random()]);
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAccessories = Accessory::query()
|
||||
->whereIn('name', $importFileBuilder->pluck('itemName'))
|
||||
->get();
|
||||
|
||||
$this->assertCount(1, $newAccessories->pluck('category_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewAccessoryWhenAccessoryWithNameExists(): void
|
||||
{
|
||||
$accessory = Accessory::factory()->create(['name' => Str::random()]);
|
||||
$importFileBuilder = ImportFileBuilder::times(2)->replace(['itemName' => $accessory->name]);
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$probablyNewAccessories = Accessory::query()
|
||||
->where('name', $importFileBuilder->pluck('itemName'))
|
||||
->get(['name']);
|
||||
|
||||
$this->assertCount(1, $probablyNewAccessories);
|
||||
$this->assertEquals($accessory->name, $probablyNewAccessories->first()->name);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewCompanyWhenCompanyAlreadyExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['companyName' => Str::random()]);
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAccessories = Accessory::query()
|
||||
->where('name', $importFileBuilder->pluck('itemName'))
|
||||
->get(['company_id']);
|
||||
|
||||
$this->assertCount(1, $newAccessories->pluck('company_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewLocationWhenLocationAlreadyExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['location' => Str::random()]);
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAccessories = Accessory::query()
|
||||
->where('name', $importFileBuilder->pluck('itemName'))
|
||||
->get(['location_id']);
|
||||
|
||||
$this->assertCount(1, $newAccessories->pluck('location_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewManufacturerWhenManufacturerAlreadyExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['manufacturerName' => $this->faker->company]);
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAccessories = Accessory::query()
|
||||
->where('name', $importFileBuilder->pluck('itemName'))
|
||||
->get(['manufacturer_id']);
|
||||
|
||||
$this->assertCount(1, $newAccessories->pluck('manufacturer_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewSupplierWhenSupplierAlreadyExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['supplierName' => $this->faker->company]);
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAccessories = Accessory::query()
|
||||
->where('name', $importFileBuilder->pluck('itemName'))
|
||||
->get(['supplier_id']);
|
||||
|
||||
$this->assertCount(1, $newAccessories->pluck('supplier_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function whenColumnsAreMissingInImportFile(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new()->forget(['minimumAmount', 'purchaseCost', 'purchaseDate']);
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAccessory = Accessory::query()
|
||||
->where('name', $importFileBuilder->firstRow()['itemName'])
|
||||
->sole();
|
||||
|
||||
$this->assertNull($newAccessory->min_amt);
|
||||
$this->assertNull($newAccessory->purchase_date);
|
||||
$this->assertNull($newAccessory->purchase_cost);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function whenRequiredColumnsAreMissingInImportFile(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new()->forget(['itemName', 'quantity', 'category']);
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertInternalServerError()
|
||||
->assertExactJson([
|
||||
'status' => 'import-errors',
|
||||
'payload' => null,
|
||||
'messages' => [
|
||||
'' => [
|
||||
'Accessory' => [
|
||||
'name' => ['The name field is required.'],
|
||||
'qty' => ['The qty field must be at least 1.'],
|
||||
'category_id' => ['The category id field is required.']
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updateAccessoryFromImport(): void
|
||||
{
|
||||
$accessory = Accessory::factory()->create(['name' => Str::random()])->refresh();
|
||||
$importFileBuilder = ImportFileBuilder::new(['itemName' => $accessory->name]);
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk();
|
||||
|
||||
$updatedAccessory = Accessory::query()->find($accessory->id);
|
||||
$updatedAttributes = [
|
||||
'name', 'company_id', 'qty', 'purchase_date', 'purchase_cost',
|
||||
'order_number', 'notes', 'category_id', 'manufacturer_id', 'supplier_id',
|
||||
'location_id', 'model_number', 'updated_at'
|
||||
];
|
||||
|
||||
$this->assertEquals($row['itemName'], $updatedAccessory->name);
|
||||
$this->assertEquals($row['companyName'], $updatedAccessory->company->name);
|
||||
$this->assertEquals($row['quantity'], $updatedAccessory->qty);
|
||||
$this->assertEquals($row['purchaseDate'], $updatedAccessory->purchase_date->toDateString());
|
||||
$this->assertEquals($row['purchaseCost'], $updatedAccessory->purchase_cost);
|
||||
$this->assertEquals($row['orderNumber'], $updatedAccessory->order_number);
|
||||
$this->assertEquals($row['notes'], $updatedAccessory->notes);
|
||||
$this->assertEquals($row['category'], $updatedAccessory->category->name);
|
||||
$this->assertEquals('accessory', $updatedAccessory->category->category_type);
|
||||
$this->assertEquals($row['manufacturerName'], $updatedAccessory->manufacturer->name);
|
||||
$this->assertEquals($row['supplierName'], $updatedAccessory->supplier->name);
|
||||
$this->assertEquals($row['location'], $updatedAccessory->location->name);
|
||||
$this->assertEquals($row['modelNumber'], $updatedAccessory->model_number);
|
||||
|
||||
$this->assertEquals(
|
||||
Arr::except($accessory->attributesToArray(), $updatedAttributes),
|
||||
Arr::except($updatedAccessory->attributesToArray(), $updatedAttributes),
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function whenImportFileContainsEmptyValues(): void
|
||||
{
|
||||
$accessory = Accessory::factory()->create(['name' => Str::random()]);
|
||||
$accessory->refresh();
|
||||
|
||||
$importFileBuilder = ImportFileBuilder::new([
|
||||
'companyName' => ' ',
|
||||
'purchaseDate' => ' ',
|
||||
'purchaseCost' => '',
|
||||
'location' => '',
|
||||
'companyName' => '',
|
||||
'orderNumber' => '',
|
||||
'category' => '',
|
||||
'quantity' => '',
|
||||
'manufacturerName' => '',
|
||||
'supplierName' => '',
|
||||
'notes' => '',
|
||||
'requestAble' => '',
|
||||
'minimumAmount' => '',
|
||||
'modelNumber' => ''
|
||||
]);
|
||||
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertInternalServerError()
|
||||
->assertExactJson([
|
||||
'status' => 'import-errors',
|
||||
'payload' => null,
|
||||
'messages' => [
|
||||
$importFileBuilder->firstRow()['itemName'] => [
|
||||
'Accessory' => [
|
||||
'qty' => ['The qty field must be at least 1.'],
|
||||
'category_id' => ['The category id field is required.']
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$importFileBuilder->replace(['itemName' => $accessory->name]);
|
||||
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk();
|
||||
|
||||
$updatedAccessory = clone $accessory;
|
||||
$updatedAccessory->refresh();
|
||||
|
||||
$this->assertEquals($accessory->toArray(), $updatedAccessory->toArray());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function customColumnMapping(): void
|
||||
{
|
||||
$faker = ImportFileBuilder::new()->definition();
|
||||
$row = [
|
||||
'itemName' => $faker['modelNumber'],
|
||||
'purchaseDate' => $faker['notes'],
|
||||
'purchaseCost' => $faker['location'],
|
||||
'location' => $faker['purchaseCost'],
|
||||
'companyName' => $faker['orderNumber'],
|
||||
'orderNumber' => $faker['companyName'],
|
||||
'category' => $faker['manufacturerName'],
|
||||
'manufacturerName' => $faker['category'],
|
||||
'notes' => $faker['purchaseDate'],
|
||||
'minimumAmount' => $faker['supplierName'],
|
||||
'modelNumber' => $faker['itemName'],
|
||||
'quantity' => $faker['quantity']
|
||||
];
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
$import = Import::factory()->accessory()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse([
|
||||
'import' => $import->id,
|
||||
'column-mappings' => [
|
||||
'Item Name' => 'model_number',
|
||||
'Purchase Date' => 'notes',
|
||||
'Purchase Cost' => 'location',
|
||||
'Location' => 'purchase_cost',
|
||||
'Company' => 'order_number',
|
||||
'Order Number' => 'company',
|
||||
'Category' => 'manufacturer',
|
||||
'Manufacturer' => 'category',
|
||||
'Supplier' => 'min_amt',
|
||||
'Notes' => 'purchase_date',
|
||||
'Min QTY' => 'supplier',
|
||||
'Model Number' => 'item_name',
|
||||
'Quantity' => 'quantity'
|
||||
]
|
||||
])->assertOk();
|
||||
|
||||
$newAccessory = Accessory::query()
|
||||
->with(['location', 'category', 'manufacturer', 'supplier'])
|
||||
->where('name', $row['modelNumber'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals($row['modelNumber'], $newAccessory->name);
|
||||
$this->assertEquals($row['itemName'], $newAccessory->model_number);
|
||||
$this->assertEquals($row['quantity'], $newAccessory->qty);
|
||||
$this->assertEquals($row['notes'], $newAccessory->purchase_date->toDateString());
|
||||
$this->assertEquals($row['location'], $newAccessory->purchase_cost);
|
||||
$this->assertEquals($row['companyName'], $newAccessory->order_number);
|
||||
$this->assertEquals($row['purchaseDate'], $newAccessory->notes);
|
||||
$this->assertEquals($row['manufacturerName'], $newAccessory->category->name);
|
||||
$this->assertEquals($row['category'], $newAccessory->manufacturer->name);
|
||||
$this->assertEquals($row['purchaseCost'], $newAccessory->location->name);
|
||||
}
|
||||
}
|
595
tests/Feature/Importing/Api/ImportAssetsTest.php
Normal file
595
tests/Feature/Importing/Api/ImportAssetsTest.php
Normal file
|
@ -0,0 +1,595 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Importing\Api;
|
||||
|
||||
use App\Models\Actionlog as ActionLog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Import;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CheckoutAssetNotification;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\Support\Importing\AssetsImportFileBuilder as ImportFileBuilder;
|
||||
use Tests\Support\Importing\CleansUpImportFiles;
|
||||
|
||||
class ImportAssetsTest extends ImportDataTestCase implements TestsPermissionsRequirement
|
||||
{
|
||||
use CleansUpImportFiles;
|
||||
use WithFaker;
|
||||
|
||||
protected function importFileResponse(array $parameters = []): TestResponse
|
||||
{
|
||||
if (!array_key_exists('import-type', $parameters)) {
|
||||
$parameters['import-type'] = 'asset';
|
||||
}
|
||||
|
||||
return parent::importFileResponse($parameters);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->create());
|
||||
|
||||
$this->importFileResponse(['import' => 44])->assertForbidden();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function userWithImportAssetsPermissionCanImportAssets(): void
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->canImport()->create());
|
||||
|
||||
$import = Import::factory()->asset()->create();
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function importAsset(): void
|
||||
{
|
||||
Notification::fake();
|
||||
|
||||
$importFileBuilder = ImportFileBuilder::new();
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertOk()
|
||||
->assertExactJson([
|
||||
'payload' => null,
|
||||
'status' => 'success',
|
||||
'messages' => ['redirect_url' => route('hardware.index')]
|
||||
]);
|
||||
|
||||
$newAsset = Asset::query()
|
||||
->with(['location', 'supplier', 'company', 'assignedAssets', 'defaultLoc', 'assetStatus', 'model.category', 'model.manufacturer'])
|
||||
->where('serial', $row['serialNumber'])
|
||||
->sole();
|
||||
|
||||
$assignee = User::query()->find($newAsset->assigned_to, ['id', 'first_name', 'last_name', 'email', 'username']);
|
||||
|
||||
$activityLogs = ActionLog::query()
|
||||
->where('item_type', Asset::class)
|
||||
->where('item_id', $newAsset->id)
|
||||
->get();
|
||||
|
||||
$this->assertCount(2, $activityLogs);
|
||||
|
||||
$this->assertEquals('checkout', $activityLogs[0]->action_type);
|
||||
$this->assertEquals(Asset::class, $activityLogs[0]->item_type);
|
||||
$this->assertEquals($assignee->id, $activityLogs[0]->target_id);
|
||||
$this->assertEquals(User::class, $activityLogs[0]->target_type);
|
||||
$this->assertEquals('Checkout from CSV Importer', $activityLogs[0]->note);
|
||||
|
||||
$this->assertEquals('create', $activityLogs[1]->action_type);
|
||||
$this->assertNull($activityLogs[1]->target_id);
|
||||
$this->assertEquals(Asset::class, $activityLogs[1]->item_type);
|
||||
$this->assertNull($activityLogs[1]->note);
|
||||
$this->assertNull($activityLogs[1]->target_type);
|
||||
|
||||
$this->assertEquals($row['assigneeFullName'], "{$assignee->first_name} {$assignee->last_name}");
|
||||
$this->assertEquals($row['assigneeEmail'], $assignee->email);
|
||||
$this->assertEquals($row['assigneeUsername'], $assignee->username);
|
||||
|
||||
$this->assertEquals($row['category'], $newAsset->model->category->name);
|
||||
$this->assertEquals($row['manufacturerName'], $newAsset->model->manufacturer->name);
|
||||
$this->assertEquals($row['itemName'], $newAsset->name);
|
||||
$this->assertEquals($row['tag'], $newAsset->asset_tag);
|
||||
$this->assertEquals($row['model'], $newAsset->model->name);
|
||||
$this->assertEquals($row['modelNumber'], $newAsset->model->model_number);
|
||||
$this->assertEquals($row['purchaseDate'], $newAsset->purchase_date->toDateString());
|
||||
$this->assertNull($newAsset->asset_eol_date);
|
||||
$this->assertEquals(0, $newAsset->eol_explicit);
|
||||
$this->assertEquals($newAsset->location_id, $newAsset->rtd_location_id);
|
||||
$this->assertEquals($row['purchaseCost'], $newAsset->purchase_cost);
|
||||
$this->assertNull($newAsset->order_number);
|
||||
$this->assertEquals('', $newAsset->image);
|
||||
$this->assertNull($newAsset->user_id);
|
||||
$this->assertEquals(1, $newAsset->physical);
|
||||
$this->assertEquals($row['status'], $newAsset->assetStatus->name);
|
||||
$this->assertEquals(0, $newAsset->archived);
|
||||
$this->assertEquals($row['warrantyInMonths'], $newAsset->warranty_months);
|
||||
$this->assertNull($newAsset->deprecate);
|
||||
$this->assertEquals($row['supplierName'], $newAsset->supplier->name);
|
||||
$this->assertEquals(0, $newAsset->requestable);
|
||||
$this->assertEquals($row['location'], $newAsset->defaultLoc->name);
|
||||
$this->assertEquals(null, $newAsset->accepted);
|
||||
$this->assertEquals(now()->toDateString(), Carbon::parse($newAsset->last_checkout)->toDateString());
|
||||
$this->assertEquals(0, $newAsset->last_checkin);
|
||||
$this->assertEquals(0, $newAsset->expected_checkin);
|
||||
$this->assertEquals($row['companyName'], $newAsset->company->name);
|
||||
$this->assertEquals(User::class, $newAsset->assigned_type);
|
||||
$this->assertNull($newAsset->last_audit_date);
|
||||
$this->assertNull($newAsset->next_audit_date);
|
||||
$this->assertEquals($row['location'], $newAsset->location->name);
|
||||
$this->assertEquals(0, $newAsset->checkin_counter);
|
||||
$this->assertEquals(1, $newAsset->checkout_counter);
|
||||
$this->assertEquals(0, $newAsset->requests_counter);
|
||||
$this->assertEquals(0, $newAsset->byod);
|
||||
|
||||
//Notes is never read.
|
||||
// $this->assertEquals($row['notes'], $newAsset->notes);
|
||||
|
||||
Notification::assertSentTo($assignee, CheckoutAssetNotification::class);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willIgnoreUnknownColumnsWhenFileContainsUnknownColumns(): void
|
||||
{
|
||||
$row = ImportFileBuilder::new()->definition();
|
||||
$row['unknownColumnInCsvFile'] = 'foo';
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewAssetWhenAssetWithSameTagAlreadyExists(): void
|
||||
{
|
||||
$asset = Asset::factory()->create(['asset_tag' => $this->faker->uuid]);
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['tag' => $asset->asset_tag]);
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertInternalServerError()
|
||||
->assertExactJson([
|
||||
'status' => 'import-errors',
|
||||
'payload' => null,
|
||||
'messages' => [
|
||||
'' => [
|
||||
'asset_tag' => [
|
||||
'asset_tag' => [
|
||||
"An asset with the asset tag {$asset->asset_tag} already exists and an update was not requested. No change was made."
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$assetsWithSameTag = Asset::query()->where('asset_tag', $asset->asset_tag)->get();
|
||||
|
||||
$this->assertCount(1, $assetsWithSameTag);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewCompanyWhenCompanyExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['companyName' => Str::random()]);
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAssets = Asset::query()
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get();
|
||||
|
||||
$this->assertCount(1, $newAssets->pluck('company_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewLocationWhenLocationExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['location' => Str::random()]);
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAssets = Asset::query()
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get();
|
||||
|
||||
$this->assertCount(1, $newAssets->pluck('location_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewSupplierWhenSupplierExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['supplierName' => $this->faker->company]);
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAssets = Asset::query()
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get(['supplier_id']);
|
||||
|
||||
$this->assertCount(1, $newAssets->pluck('supplier_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewManufacturerWhenManufacturerExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['manufacturerName' => $this->faker->company]);
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAssets = Asset::query()
|
||||
->with('model.manufacturer')
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get();
|
||||
|
||||
$this->assertCount(1, $newAssets->pluck('model.manufacturer_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateCategoryWhenCategoryExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['category' => $this->faker->company]);
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAssets = Asset::query()
|
||||
->with('model.category')
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get();
|
||||
|
||||
$this->assertCount(1, $newAssets->pluck('model.category_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewAssetModelWhenAssetModelExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['model' => Str::random()]);
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAssets = Asset::query()
|
||||
->with('model')
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get();
|
||||
|
||||
$this->assertCount(1, $newAssets->pluck('model.name')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function whenColumnsAreMissingInImportFile(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times()->forget([
|
||||
'purchaseCost',
|
||||
'purchaseDate',
|
||||
'status'
|
||||
]);
|
||||
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAsset = Asset::query()
|
||||
->with(['assetStatus'])
|
||||
->where('serial', $importFileBuilder->firstRow()['serialNumber'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals('Ready to Deploy', $newAsset->assetStatus->name);
|
||||
$this->assertNull($newAsset->purchase_date);
|
||||
$this->assertNull($newAsset->purchase_cost);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willFormatValues(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new([
|
||||
'warrantyInMonths' => '3 months',
|
||||
'purchaseDate' => '2022/10/10'
|
||||
]);
|
||||
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAsset = Asset::query()
|
||||
->where('serial', $importFileBuilder->firstRow()['serialNumber'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals(3, $newAsset->warranty_months);
|
||||
$this->assertEquals('2022-10-10', $newAsset->purchase_date->toDateString());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function whenRequiredColumnsAreMissingInImportFile(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(2)
|
||||
->forget(['tag'])
|
||||
->replace(['model' => '']);
|
||||
|
||||
$rows = $importFileBuilder->all();
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertInternalServerError()
|
||||
->assertJson([
|
||||
'status' => 'import-errors',
|
||||
'payload' => null,
|
||||
'messages' => [
|
||||
$rows[0]['itemName'] => [
|
||||
"Asset \"{$rows[0]['itemName']}\"" => [
|
||||
'asset_tag' => [
|
||||
'The asset tag field must be at least 1 characters.',
|
||||
],
|
||||
'model_id' => [
|
||||
'The model id field is required.'
|
||||
]
|
||||
]
|
||||
],
|
||||
$rows[1]['itemName'] => [
|
||||
"Asset \"{$rows[1]['itemName']}\"" => [
|
||||
'asset_tag' => [
|
||||
'The asset tag field must be at least 1 characters.',
|
||||
],
|
||||
'model_id' => [
|
||||
'The model id field is required.'
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$newAssets = Asset::query()
|
||||
->whereIn('serial', Arr::pluck($rows, 'serialNumber'))
|
||||
->get();
|
||||
|
||||
$this->assertCount(0, $newAssets);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updateAssetFromImport(): void
|
||||
{
|
||||
$asset = Asset::factory()->create()->refresh();
|
||||
$importFileBuilder = ImportFileBuilder::times(1)->replace(['tag' => $asset->asset_tag]);
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk();
|
||||
|
||||
$updatedAsset = Asset::query()
|
||||
->with(['location', 'supplier', 'company', 'defaultLoc', 'assetStatus', 'model.category', 'model.manufacturer'])
|
||||
->find($asset->id);
|
||||
|
||||
$assignee = User::query()->find($updatedAsset->assigned_to, ['id', 'first_name', 'last_name', 'email', 'username']);
|
||||
|
||||
$updatedAttributes = [
|
||||
'category', 'manufacturer_id', 'name', 'tag', 'model_id',
|
||||
'model_number', 'purchase_date', 'purchase_cost', 'warranty_months', 'supplier_id',
|
||||
'location_id', 'company_id', 'serial', 'assigned_to', 'status_id', 'rtd_location_id',
|
||||
'last_checkout', 'requestable', 'updated_at', 'checkout_counter', 'assigned_type'
|
||||
];
|
||||
|
||||
$this->assertEquals($row['assigneeFullName'], "{$assignee->first_name} {$assignee->last_name}");
|
||||
$this->assertEquals($row['assigneeEmail'], $assignee->email);
|
||||
$this->assertEquals($row['assigneeUsername'], $assignee->username);
|
||||
|
||||
$this->assertEquals($row['category'], $updatedAsset->model->category->name);
|
||||
$this->assertEquals($row['manufacturerName'], $updatedAsset->model->manufacturer->name);
|
||||
$this->assertEquals($row['itemName'], $updatedAsset->name);
|
||||
$this->assertEquals($row['tag'], $updatedAsset->asset_tag);
|
||||
$this->assertEquals($row['model'], $updatedAsset->model->name);
|
||||
$this->assertEquals($row['modelNumber'], $updatedAsset->model->model_number);
|
||||
$this->assertEquals($row['purchaseDate'], $updatedAsset->purchase_date->toDateString());
|
||||
$this->assertEquals($row['purchaseCost'], $updatedAsset->purchase_cost);
|
||||
$this->assertEquals($row['status'], $updatedAsset->assetStatus->name);
|
||||
$this->assertEquals($row['warrantyInMonths'], $updatedAsset->warranty_months);
|
||||
$this->assertEquals($row['supplierName'], $updatedAsset->supplier->name);
|
||||
$this->assertEquals($row['location'], $updatedAsset->defaultLoc->name);
|
||||
$this->assertEquals($row['companyName'], $updatedAsset->company->name);
|
||||
$this->assertEquals($row['location'], $updatedAsset->location->name);
|
||||
$this->assertEquals(1, $updatedAsset->checkout_counter);
|
||||
$this->assertEquals(user::class, $updatedAsset->assigned_type);
|
||||
|
||||
//RequestAble is always updated regardless of initial value.
|
||||
// $this->assertEquals($asset->requestable, $updatedAsset->requestable);
|
||||
|
||||
$this->assertEquals(
|
||||
Arr::except($asset->attributesToArray(), $updatedAttributes),
|
||||
Arr::except($updatedAsset->attributesToArray(), $updatedAttributes),
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function customColumnMapping(): void
|
||||
{
|
||||
$faker = ImportFileBuilder::new()->definition();
|
||||
$row = [
|
||||
'assigneeFullName' => $faker['supplierName'],
|
||||
'assigneeEmail' => $faker['manufacturerName'],
|
||||
'assigneeUsername' => $faker['serialNumber'],
|
||||
'category' => $faker['location'],
|
||||
'companyName' => $faker['purchaseCost'],
|
||||
'itemName' => $faker['modelNumber'],
|
||||
'location' => $faker['assigneeUsername'],
|
||||
'manufacturerName' => $faker['status'],
|
||||
'model' => $faker['itemName'],
|
||||
'modelNumber' => $faker['category'],
|
||||
'notes' => $faker['notes'],
|
||||
'purchaseCost' => $faker['model'],
|
||||
'purchaseDate' => $faker['companyName'],
|
||||
'serialNumber' => $faker['tag'],
|
||||
'supplierName' => $faker['purchaseDate'],
|
||||
'status' => $faker['warrantyInMonths'],
|
||||
'tag' => $faker['assigneeEmail'],
|
||||
'warrantyInMonths' => $faker['assigneeFullName'],
|
||||
];
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$this->importFileResponse([
|
||||
'import' => $import->id,
|
||||
'column-mappings' => [
|
||||
'Asset Tag' => 'email',
|
||||
'Category' => 'location',
|
||||
'Company' => 'purchase_cost',
|
||||
'Email' => 'manufacturer',
|
||||
'Full Name' => 'supplier',
|
||||
'Item Name' => 'model_number',
|
||||
'Location' => 'username',
|
||||
'Manufacturer' => 'status',
|
||||
'Model name' => 'item_name',
|
||||
'Model Number' => 'category',
|
||||
'Notes' => 'asset_notes',
|
||||
'Purchase Cost' => 'asset_model',
|
||||
'Purchase Date' => 'company',
|
||||
'Serial number' => 'asset_tag',
|
||||
'Status' => 'warranty_months',
|
||||
'Supplier' => 'purchase_date',
|
||||
'Username' => 'serial',
|
||||
'Warranty' => 'full_name',
|
||||
]
|
||||
])->assertOk();
|
||||
|
||||
$asset = Asset::query()
|
||||
->with(['location', 'supplier', 'company', 'assignedAssets', 'defaultLoc', 'assetStatus', 'model.category', 'model.manufacturer'])
|
||||
->where('serial', $row['assigneeUsername'])
|
||||
->sole();
|
||||
|
||||
$assignee = User::query()->find($asset->assigned_to, ['id', 'first_name', 'last_name', 'email', 'username']);
|
||||
|
||||
$this->assertEquals($row['warrantyInMonths'], "{$assignee->first_name} {$assignee->last_name}");
|
||||
$this->assertEquals($row['tag'], $assignee->email);
|
||||
$this->assertEquals($row['location'], $assignee->username);
|
||||
|
||||
$this->assertEquals($row['modelNumber'], $asset->model->category->name);
|
||||
$this->assertEquals($row['assigneeEmail'], $asset->model->manufacturer->name);
|
||||
$this->assertEquals($row['model'], $asset->name);
|
||||
$this->assertEquals($row['serialNumber'], $asset->asset_tag);
|
||||
$this->assertEquals($row['purchaseCost'], $asset->model->name);
|
||||
$this->assertEquals($row['itemName'], $asset->model->model_number);
|
||||
$this->assertEquals($row['supplierName'], $asset->purchase_date->toDateString());
|
||||
$this->assertEquals($row['companyName'], $asset->purchase_cost);
|
||||
$this->assertEquals($row['manufacturerName'], $asset->assetStatus->name);
|
||||
$this->assertEquals($row['status'], $asset->warranty_months);
|
||||
$this->assertEquals($row['assigneeFullName'], $asset->supplier->name);
|
||||
$this->assertEquals($row['category'], $asset->defaultLoc->name);
|
||||
$this->assertEquals($row['purchaseDate'], $asset->company->name);
|
||||
$this->assertEquals($row['category'], $asset->location->name);
|
||||
$this->assertEquals($row['notes'], $asset->notes);
|
||||
$this->assertNull($asset->asset_eol_date);
|
||||
$this->assertEquals(0, $asset->eol_explicit);
|
||||
$this->assertNull($asset->order_number);
|
||||
$this->assertEquals('', $asset->image);
|
||||
$this->assertNull($asset->user_id);
|
||||
$this->assertEquals(1, $asset->physical);
|
||||
$this->assertEquals(0, $asset->archived);
|
||||
$this->assertNull($asset->deprecate);
|
||||
$this->assertEquals(0, $asset->requestable);
|
||||
$this->assertEquals(null, $asset->accepted);
|
||||
$this->assertEquals(now()->toDateString(), Carbon::parse($asset->last_checkout)->toDateString());
|
||||
$this->assertEquals(0, $asset->last_checkin);
|
||||
$this->assertEquals(0, $asset->expected_checkin);
|
||||
$this->assertEquals(User::class, $asset->assigned_type);
|
||||
$this->assertNull($asset->last_audit_date);
|
||||
$this->assertNull($asset->next_audit_date);
|
||||
$this->assertEquals(0, $asset->checkin_counter);
|
||||
$this->assertEquals(1, $asset->checkout_counter);
|
||||
$this->assertEquals(0, $asset->requests_counter);
|
||||
$this->assertEquals(0, $asset->byod);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function customFields(): void
|
||||
{
|
||||
$macAddress = $this->faker->macAddress;
|
||||
|
||||
$row = ImportFileBuilder::new()->definition();
|
||||
$row['Mac Address'] = $macAddress;
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
$customField = CustomField::query()->where('name', 'Mac Address')->firstOrNew();
|
||||
|
||||
if (!$customField->exists) {
|
||||
$customField = CustomField::factory()->macAddress()->create(['db_column' => '_snipeit_mac_address_1']);
|
||||
}
|
||||
|
||||
if ($customField->field_encrypted) {
|
||||
$customField->field_encrypted = 0;
|
||||
$customField->save();
|
||||
}
|
||||
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newAsset = Asset::query()->where('serial', $importFileBuilder->firstRow()['serialNumber'])->sole();
|
||||
|
||||
$this->assertEquals($macAddress, $newAsset->getAttribute($customField->db_column));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willEncryptCustomFields(): void
|
||||
{
|
||||
$macAddress = $this->faker->macAddress;
|
||||
$row = ImportFileBuilder::new()->definition();
|
||||
|
||||
$row['Mac Address'] = $macAddress;
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
$customField = CustomField::query()->where('name', 'Mac Address')->firstOrNew();
|
||||
|
||||
if (!$customField->exists) {
|
||||
$customField = CustomField::factory()->macAddress()->create();
|
||||
}
|
||||
|
||||
if (!$customField->field_encrypted) {
|
||||
$customField->field_encrypted = 1;
|
||||
$customField->save();
|
||||
}
|
||||
|
||||
$import = Import::factory()->asset()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$asset = Asset::query()->where('serial', $importFileBuilder->firstRow()['serialNumber'])->sole();
|
||||
$encryptedMacAddress = $asset->getAttribute($customField->db_column);
|
||||
|
||||
$this->assertNotEquals($encryptedMacAddress, $macAddress);
|
||||
}
|
||||
}
|
305
tests/Feature/Importing/Api/ImportComponentsTest.php
Normal file
305
tests/Feature/Importing/Api/ImportComponentsTest.php
Normal file
|
@ -0,0 +1,305 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Importing\Api;
|
||||
|
||||
use App\Models\Actionlog as ActionLog;
|
||||
use App\Models\Component;
|
||||
use App\Models\Import;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\Support\Importing\CleansUpImportFiles;
|
||||
use Tests\Support\Importing\ComponentsImportFileBuilder as ImportFileBuilder;
|
||||
|
||||
class ImportComponentsTest extends ImportDataTestCase implements TestsPermissionsRequirement
|
||||
{
|
||||
use CleansUpImportFiles;
|
||||
use WithFaker;
|
||||
|
||||
protected function importFileResponse(array $parameters = []): TestResponse
|
||||
{
|
||||
if (!array_key_exists('import-type', $parameters)) {
|
||||
$parameters['import-type'] = 'component';
|
||||
}
|
||||
|
||||
return parent::importFileResponse($parameters);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->create());
|
||||
|
||||
$this->importFileResponse(['import' => 44])->assertForbidden();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function userWithImportAssetsPermissionCanImportComponents(): void
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->canImport()->create());
|
||||
|
||||
$import = Import::factory()->component()->create();
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function importComponents(): void
|
||||
{
|
||||
Notification::fake();
|
||||
|
||||
$importFileBuilder = ImportFileBuilder::new();
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->component()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertOk()
|
||||
->assertExactJson([
|
||||
'payload' => null,
|
||||
'status' => 'success',
|
||||
'messages' => ['redirect_url' => route('components.index')]
|
||||
]);
|
||||
|
||||
$newComponent = Component::query()
|
||||
->with(['location', 'category', 'company'])
|
||||
->where('name', $row['itemName'])
|
||||
->sole();
|
||||
|
||||
$activityLog = ActionLog::query()
|
||||
->where('item_type', Component::class)
|
||||
->where('item_id', $newComponent->id)
|
||||
->sole();
|
||||
|
||||
$this->assertEquals('create', $activityLog->action_type);
|
||||
$this->assertEquals('importer', $activityLog->action_source);
|
||||
$this->assertEquals($newComponent->company->id, $activityLog->company_id);
|
||||
|
||||
$this->assertEquals($row['itemName'], $newComponent->name);
|
||||
$this->assertEquals($row['companyName'], $newComponent->company->name);
|
||||
$this->assertEquals($row['category'], $newComponent->category->name);
|
||||
$this->assertEquals($row['location'], $newComponent->location->name);
|
||||
$this->assertNull($newComponent->supplier_id);
|
||||
$this->assertEquals($row['quantity'], $newComponent->qty);
|
||||
$this->assertEquals($row['orderNumber'], $newComponent->order_number);
|
||||
$this->assertEquals($row['purchaseDate'], $newComponent->purchase_date->toDateString());
|
||||
$this->assertEquals($row['purchaseCost'], $newComponent->purchase_cost);
|
||||
$this->assertNull($newComponent->min_amt);
|
||||
$this->assertEquals($row['serialNumber'], $newComponent->serial);
|
||||
$this->assertNull($newComponent->image);
|
||||
$this->assertNull($newComponent->notes);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willIgnoreUnknownColumnsWhenFileContainsUnknownColumns(): void
|
||||
{
|
||||
$row = ImportFileBuilder::new()->firstRow();
|
||||
$row['unknownColumnInCsvFile'] = 'foo';
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$import = Import::factory()->component()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewComponentWhenComponentWithNameAndSerialNumberExists(): void
|
||||
{
|
||||
$component = Component::factory()->create();
|
||||
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace([
|
||||
'itemName' => $component->name,
|
||||
'serialNumber' => $component->serial
|
||||
]);
|
||||
|
||||
$import = Import::factory()->component()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$probablyNewComponents = Component::query()
|
||||
->where('name', $component->name)
|
||||
->where('serial', $component->serial)
|
||||
->get(['id']);
|
||||
|
||||
$this->assertCount(1, $probablyNewComponents);
|
||||
$this->assertEquals($component->id, $probablyNewComponents->sole()->id);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewCompanyWhenCompanyExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['companyName' => Str::random()]);
|
||||
$import = Import::factory()->component()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newComponents = Component::query()
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get(['company_id']);
|
||||
|
||||
$this->assertCount(1, $newComponents->pluck('company_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewLocationWhenLocationExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['location' => Str::random()]);
|
||||
$import = Import::factory()->component()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newComponents = Component::query()
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get(['location_id']);
|
||||
|
||||
$this->assertCount(1, $newComponents->pluck('location_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewCategoryWhenCategoryExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['category' => $this->faker->company]);
|
||||
$import = Import::factory()->component()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newComponents = Component::query()
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get(['category_id']);
|
||||
|
||||
$this->assertCount(1, $newComponents->pluck('category_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function whenRequiredColumnsAreMissingInImportFile(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new()
|
||||
->replace(['category' => ''])
|
||||
->forget(['quantity']);
|
||||
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->component()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertInternalServerError()
|
||||
->assertExactJson([
|
||||
'status' => 'import-errors',
|
||||
'payload' => null,
|
||||
'messages' => [
|
||||
$row['itemName'] => [
|
||||
'Component' => [
|
||||
'qty' => ['The qty field must be at least 1.'],
|
||||
'category_id' => ['The category id field is required.']
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$newComponents = Component::query()
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get();
|
||||
|
||||
$this->assertCount(0, $newComponents);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updateComponentFromImport(): void
|
||||
{
|
||||
$component = Component::factory()->create();
|
||||
$importFileBuilder = ImportFileBuilder::new([
|
||||
'itemName' => $component->name,
|
||||
'serialNumber' => $component->serial
|
||||
]);
|
||||
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->component()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk();
|
||||
|
||||
$updatedComponent = Component::query()
|
||||
->with(['location', 'category'])
|
||||
->where('serial', $row['serialNumber'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals($row['itemName'], $updatedComponent->name);
|
||||
$this->assertEquals($row['category'], $updatedComponent->category->name);
|
||||
$this->assertEquals($row['location'], $updatedComponent->location->name);
|
||||
$this->assertEquals($component->supplier_id, $updatedComponent->supplier_id);
|
||||
$this->assertEquals($row['quantity'], $updatedComponent->qty);
|
||||
$this->assertEquals($row['orderNumber'], $updatedComponent->order_number);
|
||||
$this->assertEquals($row['purchaseDate'], $updatedComponent->purchase_date->toDateString());
|
||||
$this->assertEquals($row['purchaseCost'], $updatedComponent->purchase_cost);
|
||||
$this->assertEquals($component->min_amt, $updatedComponent->min_amt);
|
||||
$this->assertEquals($row['serialNumber'], $updatedComponent->serial);
|
||||
$this->assertEquals($component->image, $updatedComponent->image);
|
||||
$this->assertEquals($component->notes, $updatedComponent->notes);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function customColumnMapping(): void
|
||||
{
|
||||
$faker = ImportFileBuilder::new()->definition();
|
||||
$row = [
|
||||
'category' => $faker['serialNumber'],
|
||||
'companyName' => $faker['quantity'],
|
||||
'itemName' => $faker['purchaseDate'],
|
||||
'location' => $faker['purchaseCost'],
|
||||
'orderNumber' => $faker['orderNumber'],
|
||||
'purchaseCost' => $faker['category'],
|
||||
'purchaseDate' => $faker['companyName'],
|
||||
'quantity' => $faker['itemName'],
|
||||
'serialNumber' => $faker['location']
|
||||
];
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
$import = Import::factory()->component()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$this->importFileResponse([
|
||||
'import' => $import->id,
|
||||
'column-mappings' => [
|
||||
'Category' => 'serial',
|
||||
'Company' => 'quantity',
|
||||
'item Name' => 'purchase_date',
|
||||
'Location' => 'purchase_cost',
|
||||
'Order Number' => 'order_number',
|
||||
'Purchase Cost' => 'category',
|
||||
'Purchase Date' => 'company',
|
||||
'Quantity' => 'item_name',
|
||||
'Serial number' => 'location',
|
||||
]
|
||||
])->assertOk();
|
||||
|
||||
$newComponent = Component::query()
|
||||
->with(['location', 'category'])
|
||||
->where('serial', $importFileBuilder->firstRow()['category'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals($row['quantity'], $newComponent->name);
|
||||
$this->assertEquals($row['purchaseCost'], $newComponent->category->name);
|
||||
$this->assertEquals($row['serialNumber'], $newComponent->location->name);
|
||||
$this->assertNull($newComponent->supplier_id);
|
||||
$this->assertEquals($row['companyName'], $newComponent->qty);
|
||||
$this->assertEquals($row['orderNumber'], $newComponent->order_number);
|
||||
$this->assertEquals($row['itemName'], $newComponent->purchase_date->toDateString());
|
||||
$this->assertEquals($row['location'], $newComponent->purchase_cost);
|
||||
$this->assertNull($newComponent->min_amt);
|
||||
$this->assertNull($newComponent->image);
|
||||
$this->assertNull($newComponent->notes);
|
||||
}
|
||||
}
|
305
tests/Feature/Importing/Api/ImportConsumablesTest.php
Normal file
305
tests/Feature/Importing/Api/ImportConsumablesTest.php
Normal file
|
@ -0,0 +1,305 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Importing\Api;
|
||||
|
||||
use App\Models\Actionlog as ActivityLog;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Import;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\Support\Importing\CleansUpImportFiles;
|
||||
use Tests\Support\Importing\ConsumablesImportFileBuilder as ImportFileBuilder;
|
||||
|
||||
class ImportConsumablesTest extends ImportDataTestCase implements TestsPermissionsRequirement
|
||||
{
|
||||
use CleansUpImportFiles;
|
||||
use WithFaker;
|
||||
|
||||
protected function importFileResponse(array $parameters = []): TestResponse
|
||||
{
|
||||
if (!array_key_exists('import-type', $parameters)) {
|
||||
$parameters['import-type'] = 'consumable';
|
||||
}
|
||||
|
||||
return parent::importFileResponse($parameters);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->create());
|
||||
|
||||
$this->importFileResponse(['import' => 44])->assertForbidden();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function userWithImportAssetsPermissionCanImportConsumables(): void
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->canImport()->create());
|
||||
|
||||
$import = Import::factory()->consumable()->create();
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function importConsumables(): void
|
||||
{
|
||||
Notification::fake();
|
||||
|
||||
$importFileBuilder = ImportFileBuilder::new();
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->consumable()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertOk()
|
||||
->assertExactJson([
|
||||
'payload' => null,
|
||||
'status' => 'success',
|
||||
'messages' => ['redirect_url' => route('consumables.index')]
|
||||
]);
|
||||
|
||||
$newConsumable = Consumable::query()
|
||||
->with(['location', 'category', 'company'])
|
||||
->where('name', $row['itemName'])
|
||||
->sole();
|
||||
|
||||
$activityLog = ActivityLog::query()
|
||||
->where('item_type', Consumable::class)
|
||||
->where('item_id', $newConsumable->id)
|
||||
->sole();
|
||||
|
||||
$this->assertEquals('create', $activityLog->action_type);
|
||||
$this->assertEquals('importer', $activityLog->action_source);
|
||||
$this->assertEquals($newConsumable->company->id, $activityLog->company_id);
|
||||
|
||||
$this->assertEquals($row['itemName'], $newConsumable->name);
|
||||
$this->assertEquals($row['category'], $newConsumable->category->name);
|
||||
$this->assertEquals($row['location'], $newConsumable->location->name);
|
||||
$this->assertEquals($row['companyName'], $newConsumable->company->name);
|
||||
$this->assertNull($newConsumable->supplier_id);
|
||||
$this->assertFalse($newConsumable->requestable);
|
||||
$this->assertNull($newConsumable->image);
|
||||
$this->assertEquals($row['orderNumber'], $newConsumable->order_number);
|
||||
$this->assertEquals($row['purchaseDate'], $newConsumable->purchase_date->toDateString());
|
||||
$this->assertEquals($row['purchaseCost'], $newConsumable->purchase_cost);
|
||||
$this->assertNull($newConsumable->min_amt);
|
||||
$this->assertEquals('', $newConsumable->model_number);
|
||||
$this->assertNull($newConsumable->item_number);
|
||||
$this->assertNull($newConsumable->manufacturer_id);
|
||||
$this->assertNull($newConsumable->notes);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willIgnoreUnknownColumnsWhenFileContainsUnknownColumns(): void
|
||||
{
|
||||
$row = ImportFileBuilder::new()->definition();
|
||||
$row['unknownColumnInCsvFile'] = 'foo';
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$import = Import::factory()->consumable()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewConsumableWhenConsumableNameAlreadyExist(): void
|
||||
{
|
||||
$consumable = Consumable::factory()->create(['name' => Str::random()]);
|
||||
$importFileBuilder = ImportFileBuilder::new(['itemName' => $consumable->name]);
|
||||
$import = Import::factory()->consumable()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$probablyNewConsumables = Consumable::query()
|
||||
->where('name', $consumable->name)
|
||||
->get();
|
||||
|
||||
$this->assertCount(1, $probablyNewConsumables);
|
||||
$this->assertEquals($consumable->id, $probablyNewConsumables->sole()->id);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewCompanyWhenCompanyExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['companyName' => Str::random()]);
|
||||
$import = Import::factory()->consumable()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newConsumables = Consumable::query()
|
||||
->whereIn('name', $importFileBuilder->pluck('itemName'))
|
||||
->get(['company_id']);
|
||||
|
||||
$this->assertCount(1, $newConsumables->pluck('company_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewLocationWhenLocationExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['location' => Str::random()]);
|
||||
$import = Import::factory()->consumable()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newConsumables = Consumable::query()
|
||||
->whereIn('name', $importFileBuilder->pluck('itemName'))
|
||||
->get(['location_id']);
|
||||
|
||||
$this->assertCount(1, $newConsumables->pluck('location_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewCategoryWhenCategoryExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['category' => Str::random()]);
|
||||
$import = Import::factory()->consumable()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newConsumables = Consumable::query()
|
||||
->whereIn('name', $importFileBuilder->pluck('itemName'))
|
||||
->get(['category_id']);
|
||||
|
||||
$this->assertCount(1, $newConsumables->pluck('category_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function whenRequiredColumnsAreMissingInImportFile(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new(['category' => ''])->forget(['quantity', 'name']);
|
||||
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->consumable()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertInternalServerError()
|
||||
->assertExactJson([
|
||||
'status' => 'import-errors',
|
||||
'payload' => null,
|
||||
'messages' => [
|
||||
$row['itemName'] => [
|
||||
'Consumable' => [
|
||||
'category_id' => ['The category id field is required.']
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$newConsumables = Consumable::query()
|
||||
->whereIn('name', $importFileBuilder->pluck('itemName'))
|
||||
->get(['id']);
|
||||
|
||||
$this->assertCount(0, $newConsumables);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updateConsumableFromImport(): void
|
||||
{
|
||||
$consumable = Consumable::factory()->create(['name' => Str::random()]);
|
||||
$importFileBuilder = ImportFileBuilder::new(['itemName' => $consumable->name]);
|
||||
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->consumable()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk();
|
||||
|
||||
$updatedConsumable = Consumable::query()
|
||||
->with(['location', 'category', 'company'])
|
||||
->where('name', $importFileBuilder->firstRow()['itemName'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals($row['itemName'], $updatedConsumable->name);
|
||||
$this->assertEquals($row['category'], $updatedConsumable->category->name);
|
||||
$this->assertEquals($row['location'], $updatedConsumable->location->name);
|
||||
$this->assertEquals($row['companyName'], $updatedConsumable->company->name);
|
||||
$this->assertEquals($row['orderNumber'], $updatedConsumable->order_number);
|
||||
$this->assertEquals($row['purchaseDate'], $updatedConsumable->purchase_date->toDateString());
|
||||
$this->assertEquals($row['purchaseCost'], $updatedConsumable->purchase_cost);
|
||||
|
||||
$this->assertEquals($consumable->supplier_id, $updatedConsumable->supplier_id);
|
||||
$this->assertEquals($consumable->requestable, $updatedConsumable->requestable);
|
||||
$this->assertEquals($consumable->min_amt, $updatedConsumable->min_amt);
|
||||
$this->assertEquals($consumable->model_number, $updatedConsumable->model_number);
|
||||
$this->assertEquals($consumable->item_number, $updatedConsumable->item_number);
|
||||
$this->assertEquals($consumable->manufacturer_id, $updatedConsumable->manufacturer_id);
|
||||
$this->assertEquals($consumable->notes, $updatedConsumable->notes);
|
||||
$this->assertEquals($consumable->item_number, $updatedConsumable->item_number);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function customColumnMapping(): void
|
||||
{
|
||||
$faker = ImportFileBuilder::new()->definition();
|
||||
$row = [
|
||||
'category' => $faker['supplier'],
|
||||
'companyName' => $faker['quantity'],
|
||||
'itemName' => $faker['purchaseDate'],
|
||||
'location' => $faker['purchaseCost'],
|
||||
'orderNumber' => $faker['orderNumber'],
|
||||
'purchaseCost' => $faker['location'],
|
||||
'purchaseDate' => $faker['companyName'],
|
||||
'quantity' => $faker['itemName'],
|
||||
'supplier' => $faker['category']
|
||||
];
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
|
||||
$import = Import::factory()->consumable()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$this->importFileResponse([
|
||||
'import' => $import->id,
|
||||
'column-mappings' => [
|
||||
'Category' => 'supplier',
|
||||
'Company' => 'quantity',
|
||||
'item Name' => 'purchase_date',
|
||||
'Location' => 'purchase_cost',
|
||||
'Order Number' => 'order_number',
|
||||
'Purchase Cost' => 'location',
|
||||
'Purchase Date' => 'company',
|
||||
'Quantity' => 'item_name',
|
||||
'Supplier' => 'category',
|
||||
]
|
||||
])->assertOk();
|
||||
|
||||
$newConsumable = Consumable::query()
|
||||
->with(['location', 'category', 'company'])
|
||||
->where('name', $importFileBuilder->firstRow()['quantity'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals($row['supplier'], $newConsumable->category->name);
|
||||
$this->assertEquals($row['purchaseCost'], $newConsumable->location->name);
|
||||
$this->assertEquals($row['purchaseDate'], $newConsumable->company->name);
|
||||
$this->assertEquals($row['companyName'], $newConsumable->qty);
|
||||
$this->assertEquals($row['quantity'], $newConsumable->name);
|
||||
$this->assertNull($newConsumable->supplier_id);
|
||||
$this->assertFalse($newConsumable->requestable);
|
||||
$this->assertNull($newConsumable->image);
|
||||
$this->assertEquals($row['orderNumber'], $newConsumable->order_number);
|
||||
$this->assertEquals($row['itemName'], $newConsumable->purchase_date->toDateString());
|
||||
$this->assertEquals($row['location'], $newConsumable->purchase_cost);
|
||||
$this->assertNull($newConsumable->min_amt);
|
||||
$this->assertEquals('', $newConsumable->model_number);
|
||||
$this->assertNull($newConsumable->item_number);
|
||||
$this->assertNull($newConsumable->manufacturer_id);
|
||||
$this->assertNull($newConsumable->notes);
|
||||
}
|
||||
}
|
14
tests/Feature/Importing/Api/ImportDataTestCase.php
Normal file
14
tests/Feature/Importing/Api/ImportDataTestCase.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Importing\Api;
|
||||
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use Tests\TestCase;
|
||||
|
||||
abstract class ImportDataTestCase extends TestCase
|
||||
{
|
||||
protected function importFileResponse(array $parameters = []): TestResponse
|
||||
{
|
||||
return $this->postJson(route('api.imports.importFile', $parameters), $parameters);
|
||||
}
|
||||
}
|
356
tests/Feature/Importing/Api/ImportLicenseTest.php
Normal file
356
tests/Feature/Importing/Api/ImportLicenseTest.php
Normal file
|
@ -0,0 +1,356 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Importing\Api;
|
||||
|
||||
use App\Models\Actionlog as ActivityLog;
|
||||
use App\Models\Import;
|
||||
use App\Models\License;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\Support\Importing\CleansUpImportFiles;
|
||||
use Tests\Support\Importing\LicensesImportFileBuilder as ImportFileBuilder;
|
||||
|
||||
class ImportLicenseTest extends ImportDataTestCase implements TestsPermissionsRequirement
|
||||
{
|
||||
use CleansUpImportFiles;
|
||||
use WithFaker;
|
||||
|
||||
protected function importFileResponse(array $parameters = []): TestResponse
|
||||
{
|
||||
if (!array_key_exists('import-type', $parameters)) {
|
||||
$parameters['import-type'] = 'license';
|
||||
}
|
||||
|
||||
return parent::importFileResponse($parameters);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->create());
|
||||
|
||||
$this->importFileResponse(['import' => 44])->assertForbidden();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function userWithImportAssetsPermissionCanImportLicenses(): void
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->canImport()->create());
|
||||
|
||||
$import = Import::factory()->license()->create();
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function importLicenses(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new();
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->license()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertOk()
|
||||
->assertExactJson([
|
||||
'payload' => null,
|
||||
'status' => 'success',
|
||||
'messages' => ['redirect_url' => route('licenses.index')]
|
||||
]);
|
||||
|
||||
$newLicense = License::query()
|
||||
->withCasts(['reassignable' => 'bool'])
|
||||
->with(['category', 'company', 'manufacturer', 'supplier'])
|
||||
->where('serial', $row['serialNumber'])
|
||||
->sole();
|
||||
|
||||
$activityLogs = ActivityLog::query()
|
||||
->where('item_type', License::class)
|
||||
->where('item_id', $newLicense->id)
|
||||
->get();
|
||||
|
||||
$this->assertCount(2, $activityLogs);
|
||||
|
||||
$this->assertEquals($row['licenseName'], $newLicense->name);
|
||||
$this->assertEquals($row['serialNumber'], $newLicense->serial);
|
||||
$this->assertEquals($row['purchaseDate'], $newLicense->purchase_date->toDateString());
|
||||
$this->assertEquals($row['purchaseCost'], $newLicense->purchase_cost);
|
||||
$this->assertEquals($row['orderNumber'], $newLicense->order_number);
|
||||
$this->assertEquals($row['seats'], $newLicense->seats);
|
||||
$this->assertEquals($row['notes'], $newLicense->notes);
|
||||
$this->assertEquals($row['licensedToName'], $newLicense->license_name);
|
||||
$this->assertEquals($row['licensedToEmail'], $newLicense->license_email);
|
||||
$this->assertEquals($row['supplierName'], $newLicense->supplier->name);
|
||||
$this->assertEquals($row['companyName'], $newLicense->company->name);
|
||||
$this->assertEquals($row['category'], $newLicense->category->name);
|
||||
$this->assertEquals($row['expirationDate'], $newLicense->expiration_date->toDateString());
|
||||
$this->assertEquals($row['isMaintained'] === 'TRUE', $newLicense->maintained);
|
||||
$this->assertEquals($row['isReassignAble'] === 'TRUE', $newLicense->reassignable);
|
||||
$this->assertEquals('', $newLicense->purchase_order);
|
||||
$this->assertNull($newLicense->depreciation_id);
|
||||
$this->assertNull($newLicense->termination_date);
|
||||
$this->assertNull($newLicense->deprecate);
|
||||
$this->assertNull($newLicense->min_amt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willIgnoreUnknownColumnsWhenFileContainsUnknownColumns(): void
|
||||
{
|
||||
$row = ImportFileBuilder::new()->definition();
|
||||
$row['unknownColumnInCsvFile'] = 'foo';
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$import = Import::factory()->license()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewLicenseWhenNameAndSerialNumberAlreadyExist(): void
|
||||
{
|
||||
$license = License::factory()->create();
|
||||
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace([
|
||||
'itemName' => $license->name,
|
||||
'serialNumber' => $license->serial
|
||||
]);
|
||||
|
||||
$import = Import::factory()->license()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$probablyNewLicenses = License::query()
|
||||
->where('name', $license->name)
|
||||
->where('serial', $license->serial)
|
||||
->get();
|
||||
|
||||
$this->assertCount(1, $probablyNewLicenses);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function formatAttributes(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new([
|
||||
'expirationDate' => '2022/10/10'
|
||||
]);
|
||||
|
||||
$import = Import::factory()->license()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newLicense = License::query()
|
||||
->where('serial', $importFileBuilder->firstRow()['serialNumber'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals('2022-10-10', $newLicense->expiration_date->toDateString());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewCompanyWhenCompanyExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['companyName' => Str::random()]);
|
||||
$import = Import::factory()->license()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newLicenses = License::query()
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get(['company_id']);
|
||||
|
||||
$this->assertCount(1, $newLicenses->pluck('company_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewManufacturerWhenManufacturerExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['manufacturerName' => Str::random()]);
|
||||
$import = Import::factory()->license()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newLicenses = License::query()
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get(['manufacturer_id']);
|
||||
|
||||
$this->assertCount(1, $newLicenses->pluck('manufacturer_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewCategoryWhenCategoryExists(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['category' => $this->faker->company]);
|
||||
$import = Import::factory()->license()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newLicenses = License::query()
|
||||
->whereIn('serial', $importFileBuilder->pluck('serialNumber'))
|
||||
->get(['category_id']);
|
||||
|
||||
$this->assertCount(1, $newLicenses->pluck('category_id')->unique()->all());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function whenRequiredColumnsAreMissingInImportFile(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::times()
|
||||
->replace(['name' => ''])
|
||||
->forget(['seats']);
|
||||
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->license()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertInternalServerError()
|
||||
->assertExactJson([
|
||||
'status' => 'import-errors',
|
||||
'payload' => null,
|
||||
'messages' => [
|
||||
$row['licenseName'] => [
|
||||
"License \"{$row['licenseName']}\"" => [
|
||||
'seats' => ['The seats field is required.'],
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$newLicenses = License::query()
|
||||
->where('serial', $row['serialNumber'])
|
||||
->get();
|
||||
|
||||
$this->assertCount(0, $newLicenses);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updateLicenseFromImport(): void
|
||||
{
|
||||
$license = License::factory()->create();
|
||||
$importFileBuilder = ImportFileBuilder::new([
|
||||
'licenseName' => $license->name,
|
||||
'serialNumber' => $license->serial
|
||||
]);
|
||||
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->license()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk();
|
||||
|
||||
$updatedLicense = License::query()
|
||||
->with(['manufacturer', 'category', 'supplier'])
|
||||
->where('serial', $row['serialNumber'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals($row['licenseName'], $updatedLicense->name);
|
||||
$this->assertEquals($row['serialNumber'], $updatedLicense->serial);
|
||||
$this->assertEquals($row['purchaseDate'], $updatedLicense->purchase_date->toDateString());
|
||||
$this->assertEquals($row['purchaseCost'], $updatedLicense->purchase_cost);
|
||||
$this->assertEquals($row['orderNumber'], $updatedLicense->order_number);
|
||||
$this->assertEquals($row['seats'], $updatedLicense->seats);
|
||||
$this->assertEquals($row['notes'], $updatedLicense->notes);
|
||||
$this->assertEquals($row['licensedToName'], $updatedLicense->license_name);
|
||||
$this->assertEquals($row['licensedToEmail'], $updatedLicense->license_email);
|
||||
$this->assertEquals($row['supplierName'], $updatedLicense->supplier->name);
|
||||
$this->assertEquals($row['companyName'], $updatedLicense->company->name);
|
||||
$this->assertEquals($row['category'], $updatedLicense->category->name);
|
||||
$this->assertEquals($row['expirationDate'], $updatedLicense->expiration_date->toDateString());
|
||||
$this->assertEquals($row['isMaintained'] === 'TRUE', $updatedLicense->maintained);
|
||||
$this->assertEquals($row['isReassignAble'] === 'TRUE', $updatedLicense->reassignable);
|
||||
$this->assertEquals($license->purchase_order, $updatedLicense->purchase_order);
|
||||
$this->assertEquals($license->depreciation_id, $updatedLicense->depreciation_id);
|
||||
$this->assertEquals($license->termination_date, $updatedLicense->termination_date);
|
||||
$this->assertEquals($license->deprecate, $updatedLicense->deprecate);
|
||||
$this->assertEquals($license->min_amt, $updatedLicense->min_amt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function customColumnMapping(): void
|
||||
{
|
||||
$faker = ImportFileBuilder::times()->definition();
|
||||
$row = [
|
||||
'category' => $faker['supplierName'],
|
||||
'companyName' => $faker['serialNumber'],
|
||||
'expirationDate' => $faker['seats'],
|
||||
'isMaintained' => $faker['purchaseDate'],
|
||||
'isReassignAble' => $faker['purchaseCost'],
|
||||
'licensedToName' => $faker['orderNumber'],
|
||||
'licensedToEmail' => $faker['notes'],
|
||||
'licenseName' => $faker['licenseName'],
|
||||
'manufacturerName' => $faker['category'],
|
||||
'notes' => $faker['companyName'],
|
||||
'orderNumber' => $faker['expirationDate'],
|
||||
'purchaseCost' => $faker['isMaintained'],
|
||||
'purchaseDate' => $faker['isReassignAble'],
|
||||
'seats' => $faker['licensedToName'],
|
||||
'serialNumber' => $faker['licensedToEmail'],
|
||||
'supplierName' => $faker['manufacturerName']
|
||||
];
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
$import = Import::factory()->license()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$this->importFileResponse([
|
||||
'import' => $import->id,
|
||||
'column-mappings' => [
|
||||
'Category' => 'supplier',
|
||||
'Company' => 'serial',
|
||||
'expiration date' => 'seats',
|
||||
'maintained' => 'purchase_date',
|
||||
'reassignable' => 'purchase_cost',
|
||||
'Licensed To Name' => 'order_number',
|
||||
'Licensed To Email' => 'notes',
|
||||
'licenseName' => 'name',
|
||||
'manufacturer' => 'category',
|
||||
'Notes' => 'company',
|
||||
'Serial number' => 'license_email',
|
||||
'Order Number' => 'expiration_date',
|
||||
'purchase Cost' => 'maintained',
|
||||
'purchase Date' => 'reassignable',
|
||||
'seats' => 'license_name',
|
||||
'supplier' => 'manufacturer'
|
||||
]
|
||||
])->assertOk();
|
||||
|
||||
$newLicense = License::query()
|
||||
->with(['category', 'company', 'manufacturer', 'supplier'])
|
||||
->where('serial', $row['companyName'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals($row['licenseName'], $newLicense->name);
|
||||
$this->assertEquals($row['companyName'], $newLicense->serial);
|
||||
$this->assertEquals($row['isMaintained'], $newLicense->purchase_date->toDateString());
|
||||
$this->assertEquals($row['isReassignAble'], $newLicense->purchase_cost);
|
||||
$this->assertEquals($row['licensedToName'], $newLicense->order_number);
|
||||
$this->assertEquals($row['expirationDate'], $newLicense->seats);
|
||||
$this->assertEquals($row['licensedToEmail'], $newLicense->notes);
|
||||
$this->assertEquals($row['seats'], $newLicense->license_name);
|
||||
$this->assertEquals($row['serialNumber'], $newLicense->license_email);
|
||||
$this->assertEquals($row['category'], $newLicense->supplier->name);
|
||||
$this->assertEquals($row['notes'], $newLicense->company->name);
|
||||
$this->assertEquals($row['manufacturerName'], $newLicense->category->name);
|
||||
$this->assertEquals($row['orderNumber'], $newLicense->expiration_date->toDateString());
|
||||
$this->assertEquals($row['purchaseCost'] === 'TRUE', $newLicense->maintained);
|
||||
$this->assertEquals($row['purchaseDate'] === 'TRUE', $newLicense->reassignable);
|
||||
$this->assertEquals('', $newLicense->purchase_order);
|
||||
$this->assertNull($newLicense->depreciation_id);
|
||||
$this->assertNull($newLicense->termination_date);
|
||||
$this->assertNull($newLicense->deprecate);
|
||||
$this->assertNull($newLicense->min_amt);
|
||||
}
|
||||
}
|
336
tests/Feature/Importing/Api/ImportUsersTest.php
Normal file
336
tests/Feature/Importing/Api/ImportUsersTest.php
Normal file
|
@ -0,0 +1,336 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Importing\Api;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\Import;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\Support\Importing\CleansUpImportFiles;
|
||||
use Tests\Support\Importing\UsersImportFileBuilder as ImportFileBuilder;
|
||||
|
||||
class ImportUsersTest extends ImportDataTestCase implements TestsPermissionsRequirement
|
||||
{
|
||||
use CleansUpImportFiles;
|
||||
use WithFaker;
|
||||
|
||||
protected function importFileResponse(array $parameters = []): TestResponse
|
||||
{
|
||||
if (!array_key_exists('import-type', $parameters)) {
|
||||
$parameters['import-type'] = 'user';
|
||||
}
|
||||
|
||||
return parent::importFileResponse($parameters);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->create());
|
||||
|
||||
$this->importFileResponse(['import' => 44])->assertForbidden();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function userWithImportAssetsPermissionCanImportUsers(): void
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->canImport()->create());
|
||||
|
||||
$import = Import::factory()->users()->create();
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function importUsers(): void
|
||||
{
|
||||
Notification::fake();
|
||||
|
||||
$importFileBuilder = ImportFileBuilder::new();
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->users()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id, 'send-welcome' => 1])
|
||||
->assertOk()
|
||||
->assertExactJson([
|
||||
'payload' => null,
|
||||
'status' => 'success',
|
||||
'messages' => ['redirect_url' => route('users.index')]
|
||||
]);
|
||||
|
||||
$newUser = User::query()
|
||||
->with(['company', 'location'])
|
||||
->where('username', $row['username'])
|
||||
->sole();
|
||||
|
||||
Notification::assertNothingSent();
|
||||
|
||||
$this->assertEquals($row['email'], $newUser->email);
|
||||
$this->assertEquals($row['firstName'], $newUser->first_name);
|
||||
$this->assertEquals($row['lastName'], $newUser->last_name);
|
||||
$this->assertEquals($row['employeeNumber'], $newUser->employee_num);
|
||||
$this->assertEquals($row['companyName'], $newUser->company->name);
|
||||
$this->assertEquals($row['location'], $newUser->location->name);
|
||||
$this->assertEquals($row['phoneNumber'], $newUser->phone);
|
||||
$this->assertEquals($row['position'], $newUser->jobtitle);
|
||||
$this->assertTrue(Hash::isHashed($newUser->password));
|
||||
$this->assertEquals('', $newUser->website);
|
||||
$this->assertEquals('', $newUser->country);
|
||||
$this->assertEquals('', $newUser->address);
|
||||
$this->assertEquals('', $newUser->city);
|
||||
$this->assertEquals('', $newUser->state);
|
||||
$this->assertEquals('', $newUser->zip);
|
||||
$this->assertNull($newUser->permissions);
|
||||
$this->assertNull($newUser->avatar);
|
||||
$this->assertNull($newUser->notes);
|
||||
$this->assertNull($newUser->skin);
|
||||
$this->assertNull($newUser->department_id);
|
||||
$this->assertNull($newUser->two_factor_secret);
|
||||
$this->assertNull($newUser->idap_import);
|
||||
$this->assertEquals('en-US', $newUser->locale);
|
||||
$this->assertEquals(1, $newUser->show_in_list);
|
||||
$this->assertEquals(0, $newUser->two_factor_enrolled);
|
||||
$this->assertEquals(0, $newUser->two_factor_optin);
|
||||
$this->assertEquals(0, $newUser->remote);
|
||||
$this->assertEquals(0, $newUser->autoassign_licenses);
|
||||
$this->assertEquals(0, $newUser->vip);
|
||||
$this->assertEquals(0, $newUser->enable_sounds);
|
||||
$this->assertEquals(0, $newUser->enable_confetti);
|
||||
$this->assertNull($newUser->created_by);
|
||||
$this->assertNull($newUser->start_date);
|
||||
$this->assertNull($newUser->end_date);
|
||||
$this->assertNull($newUser->scim_externalid);
|
||||
$this->assertNull($newUser->manager_id);
|
||||
$this->assertNull($newUser->activation_code);
|
||||
$this->assertNull($newUser->last_login);
|
||||
$this->assertNull($newUser->persist_code);
|
||||
$this->assertNull($newUser->reset_password_code);
|
||||
$this->assertEquals(0, $newUser->activated);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willIgnoreUnknownColumnsWhenFileContainsUnknownColumns(): void
|
||||
{
|
||||
$row = ImportFileBuilder::new()->definition();
|
||||
$row['unknownColumnInCsvFile'] = 'foo';
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$import = Import::factory()->users()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willNotCreateNewUserWhenUserWithUserNameAlreadyExist(): void
|
||||
{
|
||||
$user = User::factory()->create(['username' => Str::random()]);
|
||||
$importFileBuilder = ImportFileBuilder::times(4)->replace(['username' => $user->username]);
|
||||
$import = Import::factory()->users()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$probablyNewUsers = User::query()
|
||||
->where('username', $user->username)
|
||||
->get();
|
||||
|
||||
$this->assertCount(1, $probablyNewUsers);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willGenerateUsernameWhenUsernameFieldIsMissing(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new()->forget('username');
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->users()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id])->assertOk();
|
||||
|
||||
$newUser = User::query()
|
||||
->where('email', $row['email'])
|
||||
->sole();
|
||||
|
||||
$generatedUsername = User::generateFormattedNameFromFullName("{$row['firstName']} {$row['lastName']}")['username'];
|
||||
|
||||
$this->assertEquals($generatedUsername, $newUser->username);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function willUpdateLocationOfAllAssetsAssignedToUser(): void
|
||||
{
|
||||
$user = User::factory()->create(['username' => Str::random()]);
|
||||
$assetsAssignedToUser = Asset::factory()->create(['assigned_to' => $user->id, 'assigned_type' => User::class]);
|
||||
$importFileBuilder = ImportFileBuilder::new(['username' => $user->username]);
|
||||
$import = Import::factory()->users()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk();
|
||||
|
||||
$userLocation = Location::query()->where('name', $importFileBuilder->firstRow()['location'])->sole(['id']);
|
||||
|
||||
$this->assertEquals(
|
||||
$userLocation->id,
|
||||
$assetsAssignedToUser->refresh()->location_id
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function whenRequiredColumnsAreMissingInImportFile(): void
|
||||
{
|
||||
$importFileBuilder = ImportFileBuilder::new(['firstName' => ''])->forget(['username']);
|
||||
$import = Import::factory()->users()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$this->importFileResponse(['import' => $import->id])
|
||||
->assertInternalServerError()
|
||||
->assertExactJson([
|
||||
'status' => 'import-errors',
|
||||
'payload' => null,
|
||||
'messages' => [
|
||||
'' => [
|
||||
'User' => [
|
||||
'first_name' => ['The first name field is required.'],
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$newUsers = User::query()
|
||||
->where('email', $importFileBuilder->firstRow()['email'])
|
||||
->get();
|
||||
|
||||
$this->assertCount(0, $newUsers);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updateUserFromImport(): void
|
||||
{
|
||||
$user = User::factory()->create(['username' => Str::random()])->refresh();
|
||||
$importFileBuilder = ImportFileBuilder::new(['username' => $user->username]);
|
||||
|
||||
$row = $importFileBuilder->firstRow();
|
||||
$import = Import::factory()->users()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
$this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk();
|
||||
|
||||
$updatedUser = User::query()->with(['company', 'location'])->find($user->id);
|
||||
$updatedAttributes = [
|
||||
'first_name', 'email', 'last_name', 'employee_num', 'company',
|
||||
'location_id', 'company_id', 'updated_at', 'phone', 'jobtitle'
|
||||
];
|
||||
|
||||
$this->assertEquals($row['email'], $updatedUser->email);
|
||||
$this->assertEquals($row['firstName'], $updatedUser->first_name);
|
||||
$this->assertEquals($row['lastName'], $updatedUser->last_name);
|
||||
$this->assertEquals($row['employeeNumber'], $updatedUser->employee_num);
|
||||
$this->assertEquals($row['companyName'], $updatedUser->company->name);
|
||||
$this->assertEquals($row['location'], $updatedUser->location->name);
|
||||
$this->assertEquals($row['phoneNumber'], $updatedUser->phone);
|
||||
$this->assertEquals($row['position'], $updatedUser->jobtitle);
|
||||
$this->assertTrue(Hash::isHashed($updatedUser->password));
|
||||
|
||||
$this->assertEquals(
|
||||
Arr::except($user->attributesToArray(), $updatedAttributes),
|
||||
Arr::except($updatedUser->attributesToArray(), $updatedAttributes),
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function customColumnMapping(): void
|
||||
{
|
||||
$faker = ImportFileBuilder::new()->definition();
|
||||
$row = [
|
||||
'companyName' => $faker['username'],
|
||||
'email' => $faker['position'],
|
||||
'employeeNumber' => $faker['phoneNumber'],
|
||||
'firstName' => $faker['location'],
|
||||
'lastName' => $faker['lastName'],
|
||||
'location' => $faker['firstName'],
|
||||
'phoneNumber' => $faker['employeeNumber'],
|
||||
'position' => $faker['email'],
|
||||
'username' => $faker['companyName'],
|
||||
];
|
||||
|
||||
$importFileBuilder = new ImportFileBuilder([$row]);
|
||||
$import = Import::factory()->users()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create());
|
||||
|
||||
$this->importFileResponse([
|
||||
'import' => $import->id,
|
||||
'column-mappings' => [
|
||||
'Company' => 'username',
|
||||
'email' => 'jobtitle',
|
||||
'Employee Number' => 'phone_number',
|
||||
'First Name' => 'location',
|
||||
'Last Name' => 'last_name',
|
||||
'Location' => 'first_name',
|
||||
'Phone Number' => 'employee_num',
|
||||
'Job Title' => 'email',
|
||||
'Username' => 'company',
|
||||
]
|
||||
])->assertOk();
|
||||
|
||||
$newUser = User::query()
|
||||
->with(['company', 'location'])
|
||||
->where('username', $row['companyName'])
|
||||
->sole();
|
||||
|
||||
$this->assertEquals($row['position'], $newUser->email);
|
||||
$this->assertEquals($row['location'], $newUser->first_name);
|
||||
$this->assertEquals($row['lastName'], $newUser->last_name);
|
||||
$this->assertEquals($row['email'], $newUser->jobtitle);
|
||||
$this->assertEquals($row['phoneNumber'], $newUser->employee_num);
|
||||
$this->assertEquals($row['username'], $newUser->company->name);
|
||||
$this->assertEquals($row['firstName'], $newUser->location->name);
|
||||
$this->assertEquals($row['employeeNumber'], $newUser->phone);
|
||||
$this->assertTrue(Hash::isHashed($newUser->password));
|
||||
$this->assertEquals('', $newUser->website);
|
||||
$this->assertEquals('', $newUser->country);
|
||||
$this->assertEquals('', $newUser->address);
|
||||
$this->assertEquals('', $newUser->city);
|
||||
$this->assertEquals('', $newUser->state);
|
||||
$this->assertEquals('', $newUser->zip);
|
||||
$this->assertNull($newUser->permissions);
|
||||
$this->assertNull($newUser->avatar);
|
||||
$this->assertNull($newUser->notes);
|
||||
$this->assertNull($newUser->skin);
|
||||
$this->assertNull($newUser->department_id);
|
||||
$this->assertNull($newUser->two_factor_secret);
|
||||
$this->assertNull($newUser->idap_import);
|
||||
$this->assertEquals('en-US', $newUser->locale);
|
||||
$this->assertEquals(1, $newUser->show_in_list);
|
||||
$this->assertEquals(0, $newUser->two_factor_enrolled);
|
||||
$this->assertEquals(0, $newUser->two_factor_optin);
|
||||
$this->assertEquals(0, $newUser->remote);
|
||||
$this->assertEquals(0, $newUser->autoassign_licenses);
|
||||
$this->assertEquals(0, $newUser->vip);
|
||||
$this->assertEquals(0, $newUser->enable_sounds);
|
||||
$this->assertEquals(0, $newUser->enable_confetti);
|
||||
$this->assertNull($newUser->created_by);
|
||||
$this->assertNull($newUser->start_date);
|
||||
$this->assertNull($newUser->end_date);
|
||||
$this->assertNull($newUser->scim_externalid);
|
||||
$this->assertNull($newUser->manager_id);
|
||||
$this->assertNull($newUser->activation_code);
|
||||
$this->assertNull($newUser->last_login);
|
||||
$this->assertNull($newUser->persist_code);
|
||||
$this->assertNull($newUser->reset_password_code);
|
||||
$this->assertEquals(0, $newUser->activated);
|
||||
}
|
||||
}
|
|
@ -2,22 +2,28 @@
|
|||
|
||||
namespace Tests\Feature\Settings;
|
||||
|
||||
use App\Models\Asset;
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Models\User;
|
||||
use App\Models\Setting;
|
||||
|
||||
|
||||
class AlertsSettingTest extends TestCase
|
||||
{
|
||||
public function testPermissionRequiredToViewAlertSettings()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
$this->actingAs(User::factory()->create())
|
||||
->get(route('settings.alerts.index'))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testAdminCCEmailArrayCanBeSaved()
|
||||
{
|
||||
$response = $this->actingAs(User::factory()->superuser()->create())
|
||||
->post(route('settings.alerts.save', ['alert_email' => 'me@example.com,you@example.com']))
|
||||
->assertStatus(302)
|
||||
->assertValid('alert_email')
|
||||
->assertRedirect(route('settings.index'))
|
||||
->assertSessionHasNoErrors();
|
||||
$this->followRedirects($response)->assertSee('alert-success');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Tests\Feature\Settings;
|
||||
|
||||
use App\Models\Asset;
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
|
18
tests/Feature/Settings/LabelSettingTest.php
Normal file
18
tests/Feature/Settings/LabelSettingTest.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Settings;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\User;
|
||||
|
||||
|
||||
class LabelSettingTest extends TestCase
|
||||
{
|
||||
public function testPermissionRequiredToViewLabelSettings()
|
||||
{
|
||||
$this->actingAs(User::factory()->create())
|
||||
->get(route('settings.labels.index'))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
}
|
62
tests/Feature/Settings/LdapSettingsTest.php
Normal file
62
tests/Feature/Settings/LdapSettingsTest.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Settings;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\User;
|
||||
|
||||
|
||||
class LdapSettingsTest extends TestCase
|
||||
{
|
||||
public function testPermissionRequiredToViewLdapSettings()
|
||||
{
|
||||
$this->actingAs(User::factory()->create())
|
||||
->get(route('settings.ldap.index'))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testLdapSettingsCanBeSaved()
|
||||
{
|
||||
$response = $this->actingAs(User::factory()->superuser()->create())
|
||||
->post(route('settings.ldap.save', [
|
||||
'ldap_enabled' => 1,
|
||||
'ldap_username_field' => 'samaccountname',
|
||||
'ldap_filter' => 'uid=',
|
||||
'ldap_auth_filter_query' => 'uid=',
|
||||
'ldap_uname' => 'SomeUserField',
|
||||
'ldap_pword' => 'MyAwesomePassword',
|
||||
'ldap_basedn' => 'uid=',
|
||||
'ldap_fname_field' => 'SomeFirstnameField',
|
||||
'ldap_server' => 'ldaps://ldap.example.com',
|
||||
]))
|
||||
->assertStatus(302)
|
||||
->assertValid('ldap_enabled')
|
||||
->assertRedirect(route('settings.ldap.index'))
|
||||
->assertSessionHasNoErrors();
|
||||
$this->followRedirects($response)->assertSee('alert-success');
|
||||
}
|
||||
|
||||
public function testLdapSettingsAreValidatedCorrectly()
|
||||
{
|
||||
$response = $this->actingAs(User::factory()->superuser()->create())
|
||||
->from(route('settings.ldap.index'))
|
||||
->post(route('settings.ldap.save', [
|
||||
'ldap_enabled' => 1,
|
||||
'ldap_username_field' => 'sAMAccountName',
|
||||
'ldap_filter' => '(uid=)',
|
||||
]))
|
||||
->assertStatus(302)
|
||||
->assertRedirect(route('settings.ldap.index'))
|
||||
->assertSessionHasErrors([
|
||||
'ldap_username_field',
|
||||
'ldap_auth_filter_query',
|
||||
'ldap_uname',
|
||||
'ldap_pword',
|
||||
'ldap_basedn',
|
||||
'ldap_fname_field',
|
||||
'ldap_server',
|
||||
]);
|
||||
$this->followRedirects($response)->assertSee('alert-danger');
|
||||
}
|
||||
|
||||
}
|
18
tests/Feature/Settings/SecuritySettingTest.php
Normal file
18
tests/Feature/Settings/SecuritySettingTest.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Settings;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\User;
|
||||
|
||||
|
||||
class SecuritySettingTest extends TestCase
|
||||
{
|
||||
public function testPermissionRequiredToViewSecuritySettings()
|
||||
{
|
||||
$this->actingAs(User::factory()->create())
|
||||
->get(route('settings.security.index'))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
}
|
|
@ -100,5 +100,26 @@ trait CustomTestMacros
|
|||
return $this;
|
||||
}
|
||||
);
|
||||
|
||||
TestResponse::macro(
|
||||
'assertMessagesContains',
|
||||
function (array|string $keys) {
|
||||
Assert::assertArrayHasKey('messages', $this, 'Response did not contain any messages');
|
||||
|
||||
if (is_string($keys)) {
|
||||
$keys = [$keys];
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
Assert::assertArrayHasKey(
|
||||
$key,
|
||||
$this['messages'],
|
||||
"Response messages did not contain the key: {$key}"
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
74
tests/Support/Importing/AccessoriesImportFileBuilder.php
Normal file
74
tests/Support/Importing/AccessoriesImportFileBuilder.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Support\Importing;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Build an accessories import file at runtime for testing.
|
||||
*
|
||||
* @template Row of array{
|
||||
* category?: string,
|
||||
* companyName?: string,
|
||||
* itemName?: string,
|
||||
* location?: string,
|
||||
* manufacturerName?: string,
|
||||
* modelNumber?: string,
|
||||
* notes?: string,
|
||||
* orderNumber?: string,
|
||||
* purchaseCost?: int,
|
||||
* purchaseDate?: string,
|
||||
* quantity?: int,
|
||||
* supplierName?: string
|
||||
* }
|
||||
*
|
||||
* @extends FileBuilder<Row>
|
||||
*/
|
||||
class AccessoriesImportFileBuilder extends FileBuilder
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getDictionary(): array
|
||||
{
|
||||
return [
|
||||
'category' => 'Category',
|
||||
'companyName' => 'Company',
|
||||
'itemName' => 'Item Name',
|
||||
'location' => 'Location',
|
||||
'manufacturerName' => 'Manufacturer',
|
||||
'modelNumber' => 'Model Number',
|
||||
'notes' => 'Notes',
|
||||
'orderNumber' => 'Order Number',
|
||||
'purchaseCost' => 'Purchase Cost',
|
||||
'purchaseDate' => 'Purchase Date',
|
||||
'quantity' => 'Quantity',
|
||||
'supplierName' => 'Supplier',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$faker = fake();
|
||||
|
||||
return [
|
||||
'category' => Str::random(),
|
||||
'companyName' => Str::random(),
|
||||
'itemName' => Str::random(),
|
||||
'location' => "{$faker->city}, {$faker->country}",
|
||||
'manufacturerName' => $faker->company,
|
||||
'modelNumber' => Str::random(),
|
||||
'notes' => $faker->sentence,
|
||||
'orderNumber' => Str::random(),
|
||||
'purchaseDate' => $faker->date(),
|
||||
'purchaseCost' => rand(1, 100),
|
||||
'quantity' => rand(1, 100),
|
||||
'supplierName' => $faker->company,
|
||||
];
|
||||
}
|
||||
}
|
92
tests/Support/Importing/AssetsImportFileBuilder.php
Normal file
92
tests/Support/Importing/AssetsImportFileBuilder.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Support\Importing;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Build an assets import file at runtime for testing.
|
||||
*
|
||||
* @template Row of array{
|
||||
* assigneeFullName?: string,
|
||||
* assigneeEmail?: string,
|
||||
* assigneeUsername?: string,
|
||||
* category?: string,
|
||||
* companyName?: string,
|
||||
* itemName?: string,
|
||||
* location?: string,
|
||||
* manufacturerName?: int,
|
||||
* model?: string,
|
||||
* modelNumber?: string,
|
||||
* notes?: string,
|
||||
* purchaseCost?: int,
|
||||
* purchaseDate?: string,
|
||||
* serialNumber?: string,
|
||||
* supplierName?: string,
|
||||
* status?: string,
|
||||
* tag?: string,
|
||||
* warrantyInMonths?: int,
|
||||
* }
|
||||
*
|
||||
* @extends FileBuilder<Row>
|
||||
*/
|
||||
class AssetsImportFileBuilder extends FileBuilder
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getDictionary(): array
|
||||
{
|
||||
return [
|
||||
'assigneeFullName' => 'Full Name',
|
||||
'assigneeEmail' => 'Email',
|
||||
'assigneeUsername' => 'Username',
|
||||
'category' => 'Category',
|
||||
'companyName' => 'Company',
|
||||
'itemName' => 'item Name',
|
||||
'location' => 'Location',
|
||||
'manufacturerName' => 'Manufacturer',
|
||||
'model' => 'Model name',
|
||||
'modelNumber' => 'Model Number',
|
||||
'notes' => 'Notes',
|
||||
'purchaseCost' => 'Purchase Cost',
|
||||
'purchaseDate' => 'Purchase Date',
|
||||
'serialNumber' => 'Serial number',
|
||||
'supplierName' => 'Supplier',
|
||||
'status' => 'Status',
|
||||
'tag' => 'Asset Tag',
|
||||
'warrantyInMonths' => 'Warranty',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$faker = fake();
|
||||
|
||||
return [
|
||||
'assigneeFullName' => $faker->name,
|
||||
'assigneeEmail' => $faker->email,
|
||||
'assigneeUsername' => $faker->userName,
|
||||
'category' => Str::random(),
|
||||
'companyName' => Str::random() . " {$faker->companySuffix}",
|
||||
'itemName' => Str::random(),
|
||||
'location' => "{$faker->country},{$faker->city}",
|
||||
'manufacturerName' => $faker->company,
|
||||
'model' => Str::random(),
|
||||
'modelNumber' => Str::random(),
|
||||
'notes' => $faker->sentence(5),
|
||||
'purchaseCost' => rand(1, 100_000),
|
||||
'purchaseDate' => $faker->date,
|
||||
'serialNumber' => $faker->uuid,
|
||||
'supplierName' => $faker->company,
|
||||
'status' => $faker->randomElement(['Ready to Deploy', 'Archived', 'Pending']),
|
||||
'tag' => Str::random(),
|
||||
'warrantyInMonths' => rand(1, 12),
|
||||
];
|
||||
}
|
||||
}
|
20
tests/Support/Importing/CleansUpImportFiles.php
Normal file
20
tests/Support/Importing/CleansUpImportFiles.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Support\Importing;
|
||||
|
||||
use App\Models\Import;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
trait CleansUpImportFiles
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Import::created(function (Import $import) {
|
||||
$this->beforeApplicationDestroyed(function () use ($import) {
|
||||
Storage::delete('private_uploads/imports/' . $import->file_path);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
65
tests/Support/Importing/ComponentsImportFileBuilder.php
Normal file
65
tests/Support/Importing/ComponentsImportFileBuilder.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Support\Importing;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Build a components import file at runtime for testing.
|
||||
*
|
||||
* @template Row of array{
|
||||
* category?: string,
|
||||
* companyName?: string,
|
||||
* itemName?: string,
|
||||
* location?: string,
|
||||
* orderNumber?: string,
|
||||
* purchaseCost?: int,
|
||||
* purchaseDate?: string,
|
||||
* quantity?: int,
|
||||
* serialNumber?: string,
|
||||
* }
|
||||
*
|
||||
* @extends FileBuilder<Row>
|
||||
*/
|
||||
class ComponentsImportFileBuilder extends FileBuilder
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getDictionary(): array
|
||||
{
|
||||
return [
|
||||
'category' => 'Category',
|
||||
'companyName' => 'Company',
|
||||
'itemName' => 'item Name',
|
||||
'location' => 'Location',
|
||||
'orderNumber' => 'Order Number',
|
||||
'purchaseCost' => 'Purchase Cost',
|
||||
'purchaseDate' => 'Purchase Date',
|
||||
'quantity' => 'Quantity',
|
||||
'serialNumber' => 'Serial number',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$faker = fake();
|
||||
|
||||
return [
|
||||
'category' => Str::random(),
|
||||
'companyName' => Str::random() . " {$faker->companySuffix}",
|
||||
'itemName' => Str::random(),
|
||||
'location' => "{$faker->city}, {$faker->country}",
|
||||
'orderNumber' => "ON:COM:{$faker->uuid}",
|
||||
'purchaseCost' => rand(1, 100_000),
|
||||
'purchaseDate' => $faker->date,
|
||||
'quantity' => rand(1, 100_000),
|
||||
'serialNumber' => 'SN:COM:' . Str::random(),
|
||||
];
|
||||
}
|
||||
}
|
65
tests/Support/Importing/ConsumablesImportFileBuilder.php
Normal file
65
tests/Support/Importing/ConsumablesImportFileBuilder.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Support\Importing;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Build a consumables import file at runtime for testing.
|
||||
*
|
||||
* @template Row of array{
|
||||
* category?: string,
|
||||
* companyName?: string,
|
||||
* itemName?: string,
|
||||
* location?: string,
|
||||
* orderNumber?: string,
|
||||
* purchaseCost?: int,
|
||||
* purchaseDate?: string,
|
||||
* quantity?: int,
|
||||
* supplier?: string,
|
||||
* }
|
||||
*
|
||||
* @extends FileBuilder<Row>
|
||||
*/
|
||||
class ConsumablesImportFileBuilder extends FileBuilder
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getDictionary(): array
|
||||
{
|
||||
return [
|
||||
'category' => 'Category',
|
||||
'companyName' => 'Company',
|
||||
'itemName' => 'item Name',
|
||||
'location' => 'Location',
|
||||
'orderNumber' => 'Order Number',
|
||||
'purchaseCost' => 'Purchase Cost',
|
||||
'purchaseDate' => 'Purchase Date',
|
||||
'quantity' => 'Quantity',
|
||||
'supplier' => 'Supplier',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$faker = fake();
|
||||
|
||||
return [
|
||||
'category' => Str::random(),
|
||||
'companyName' => Str::random() . " {$faker->companySuffix}",
|
||||
'itemName' => Str::random(),
|
||||
'location' => "{$faker->city}, {$faker->country}",
|
||||
'orderNumber' => "ON:CON:{$faker->uuid}",
|
||||
'purchaseCost' => rand(1, 100_000),
|
||||
'purchaseDate' => $faker->date,
|
||||
'quantity' => rand(1, 100_000),
|
||||
'supplier' => Str::random() . " {$faker->companySuffix}",
|
||||
];
|
||||
}
|
||||
}
|
249
tests/Support/Importing/FileBuilder.php
Normal file
249
tests/Support/Importing/FileBuilder.php
Normal file
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Support\Importing;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Collection;
|
||||
use League\Csv\Reader;
|
||||
use OutOfBoundsException;
|
||||
|
||||
/**
|
||||
* @template Row of array
|
||||
*/
|
||||
abstract class FileBuilder
|
||||
{
|
||||
/**
|
||||
* The import file rows.
|
||||
*
|
||||
* @var Collection<Row>
|
||||
*/
|
||||
protected Collection $rows;
|
||||
|
||||
/**
|
||||
* Define the builders default row.
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
abstract public function definition();
|
||||
|
||||
/**
|
||||
* @param array<Row> $rows
|
||||
*/
|
||||
public function __construct(array $rows = [])
|
||||
{
|
||||
$this->rows = new Collection($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new file builder instance.
|
||||
*
|
||||
* @param Row $attributes
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function new(array $attributes = [])
|
||||
{
|
||||
$instance = new static;
|
||||
|
||||
return $instance->push($instance->definition())->replace($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new file builder instance from an import file.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function fromFile(string $filepath)
|
||||
{
|
||||
$instance = new static;
|
||||
|
||||
$reader = Reader::createFromPath($filepath);
|
||||
$importFileHeaders = $reader->first();
|
||||
$dictionary = array_flip($instance->getDictionary());
|
||||
|
||||
foreach ($reader->getRecords() as $key => $record) {
|
||||
$row = [];
|
||||
|
||||
//Skip header.
|
||||
if ($key === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($record as $index => $value) {
|
||||
$columnNameInImportFile = $importFileHeaders[$index];
|
||||
|
||||
//Try to map the value to a dictionary or use the file's
|
||||
//column if the key is not defined in the dictionary.
|
||||
$row[$dictionary[$columnNameInImportFile] ?? $columnNameInImportFile] = $value;
|
||||
}
|
||||
|
||||
$instance->push($row);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new builder instance for the given number of rows.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function times(int $amountOfRows = 1)
|
||||
{
|
||||
$instance = new static;
|
||||
|
||||
for ($i = 1; $i <= $amountOfRows; $i++) {
|
||||
$instance->push($instance->definition());
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* The the dictionary for mapping row keys to the corresponding import file headers.
|
||||
*
|
||||
* @return array<string,string>
|
||||
*/
|
||||
protected function getDictionary(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new row.
|
||||
*
|
||||
* @param Row $row
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function push(array $row)
|
||||
{
|
||||
if (!empty($row)) {
|
||||
$this->rows->push($row);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pluck an array of values from the rows.
|
||||
*/
|
||||
public function pluck(string $key): array
|
||||
{
|
||||
return $this->rows->pluck($key)->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the keys in each row with the values of the given replacement if they exist.
|
||||
*
|
||||
* @param array<Row> $replacement
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function replace(array $replacement)
|
||||
{
|
||||
$this->rows = $this->rows->map(function (array $row) use ($replacement) {
|
||||
foreach ($replacement as $key => $value) {
|
||||
if (!array_key_exists($key, $row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row[$key] = $value;
|
||||
}
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the the given keys from all rows.
|
||||
*
|
||||
* @param string|array<string> $keys
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function forget(array|string $keys)
|
||||
{
|
||||
$keys = (array) $keys;
|
||||
|
||||
$this->rows = $this->rows->map(function (array $row) use ($keys) {
|
||||
foreach ($keys as $key) {
|
||||
unset($row[$key]);
|
||||
}
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toCsv(): array
|
||||
{
|
||||
if ($this->rows->isEmpty()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
$rows = $this->rows;
|
||||
$dictionary = $this->getDictionary();
|
||||
|
||||
foreach (array_keys($rows->first()) as $key) {
|
||||
$headers[] = $dictionary[$key] ?? $key;
|
||||
}
|
||||
|
||||
return $rows
|
||||
->map(fn (array $row) => array_values(array_combine($headers, $row)))
|
||||
->prepend($headers)
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the rows to the imports folder as a csv file.
|
||||
*
|
||||
* @return string The filename.
|
||||
*/
|
||||
public function saveToImportsDirectory(?string $filename = null): string
|
||||
{
|
||||
$filename ??= Str::random(40) . '.csv';
|
||||
|
||||
try {
|
||||
$stream = fopen(config('app.private_uploads') . "/imports/{$filename}", 'w');
|
||||
|
||||
foreach ($this->toCsv() as $row) {
|
||||
fputcsv($stream, $row);
|
||||
}
|
||||
|
||||
return $filename;
|
||||
} finally {
|
||||
if (is_resource($stream)) {
|
||||
fclose($stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first row of the import file.
|
||||
*
|
||||
* @throws OutOfBoundsException
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
public function firstRow(): array
|
||||
{
|
||||
return $this->rows->first(null, fn () => throw new OutOfBoundsException('Could not retrieve row from collection.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the all the rows of the import file.
|
||||
*
|
||||
* @return array<Row>
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->rows->all();
|
||||
}
|
||||
}
|
86
tests/Support/Importing/LicensesImportFileBuilder.php
Normal file
86
tests/Support/Importing/LicensesImportFileBuilder.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Support\Importing;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Build a consumables import file at runtime for testing.
|
||||
*
|
||||
* @template Row of array{
|
||||
* category?: string,
|
||||
* companyName?: string,
|
||||
* expirationDate?: string,
|
||||
* isMaintained?: bool,
|
||||
* isReassignAble?: bool,
|
||||
* licensedToName?: string,
|
||||
* licensedToEmail?: email,
|
||||
* licenseName?: string,
|
||||
* manufacturerName?: string,
|
||||
* notes?: string,
|
||||
* orderNumber?: string,
|
||||
* purchaseCost?: int,
|
||||
* purchaseDate?: string,
|
||||
* seats?: int,
|
||||
* serialNumber?: string,
|
||||
* supplierName?: string
|
||||
* }
|
||||
*
|
||||
* @extends FileBuilder<Row>
|
||||
*/
|
||||
class LicensesImportFileBuilder extends FileBuilder
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getDictionary(): array
|
||||
{
|
||||
return [
|
||||
'category' => 'Category',
|
||||
'companyName' => 'Company',
|
||||
'expirationDate' => 'expiration date',
|
||||
'isMaintained' => 'maintained',
|
||||
'isReassignAble' => 'reassignable',
|
||||
'licensedToName' => 'Licensed To Name',
|
||||
'licensedToEmail' => 'Licensed to Email',
|
||||
'licenseName' => 'Item name',
|
||||
'manufacturerName' => 'manufacturer',
|
||||
'notes' => 'notes',
|
||||
'orderNumber' => 'Order Number',
|
||||
'purchaseCost' => 'Purchase Cost',
|
||||
'purchaseDate' => 'Purchase Date',
|
||||
'seats' => 'seats',
|
||||
'serialNumber' => 'Serial number',
|
||||
'supplierName' => 'supplier',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$faker = fake();
|
||||
|
||||
return [
|
||||
'category' => Str::random(),
|
||||
'companyName' => Str::random() . " {$faker->companySuffix}",
|
||||
'expirationDate' => $faker->date,
|
||||
'isMaintained' => $faker->randomElement(['TRUE', 'FALSE']),
|
||||
'isReassignAble' => $faker->randomElement(['TRUE', 'FALSE']),
|
||||
'licensedToName' => $faker->name,
|
||||
'licensedToEmail' => $faker->email,
|
||||
'licenseName' => $faker->company,
|
||||
'manufacturerName' => $faker->company,
|
||||
'notes' => $faker->sentence,
|
||||
'orderNumber' => "ON:LIC:{$faker->uuid}",
|
||||
'purchaseCost' => rand(1, 100_000),
|
||||
'purchaseDate' => $faker->date,
|
||||
'seats' => rand(1, 10),
|
||||
'serialNumber' => 'SN:LIC:' . Str::random(),
|
||||
'supplierName' => $faker->company,
|
||||
];
|
||||
}
|
||||
}
|
65
tests/Support/Importing/UsersImportFileBuilder.php
Normal file
65
tests/Support/Importing/UsersImportFileBuilder.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Support\Importing;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Build a users import file at runtime for testing.
|
||||
*
|
||||
* @template Row of array{
|
||||
* companyName?: string,
|
||||
* email?: string,
|
||||
* employeeNumber?: int,
|
||||
* firstName?: string,
|
||||
* lastName?: string,
|
||||
* location?: string,
|
||||
* phoneNumber?: string,
|
||||
* position?: string,
|
||||
* username?: string,
|
||||
* }
|
||||
*
|
||||
* @extends FileBuilder<Row>
|
||||
*/
|
||||
class UsersImportFileBuilder extends FileBuilder
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getDictionary(): array
|
||||
{
|
||||
return [
|
||||
'companyName' => 'Company',
|
||||
'email' => 'email',
|
||||
'employeeNumber' => 'Employee Number',
|
||||
'firstName' => 'First Name',
|
||||
'lastName' => 'Last Name',
|
||||
'location' => 'Location',
|
||||
'phoneNumber' => 'Phone Number',
|
||||
'position' => 'Job Title',
|
||||
'username' => 'Username',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$faker = fake();
|
||||
|
||||
return [
|
||||
'companyName' => $faker->company,
|
||||
'email' => Str::random(32) . "@{$faker->freeEmailDomain}",
|
||||
'employeeNumber' => $faker->uuid,
|
||||
'firstName' => $faker->firstName,
|
||||
'lastName' => $faker->lastName,
|
||||
'location' => "{$faker->city}, {$faker->country}",
|
||||
'phoneNumber' => $faker->phoneNumber,
|
||||
'position' => $faker->jobTitle,
|
||||
'username' => Str::random(),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -96,7 +96,7 @@ class LdapTest extends TestCase
|
|||
"count" => 1,
|
||||
0 => [
|
||||
'sn' => 'Surname',
|
||||
'firstName' => 'FirstName'
|
||||
'firstname' => 'FirstName'
|
||||
]
|
||||
]
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue