Repository: snipe/snipe-it
Pull request #16637: Fixed #8188 - Added Last Name as an email format
Author: login: akemidx
Labels: name: frontend
name: backend
name: translations
name: tests
Created: 2025-04-04T03:40:40Z | Merged: 2025-04-09T20:27:11Z

Making it so you can choose only Last Name

<img width="1237" alt="Screenshot 2025-04-03 at 11 42 14 PM" src="https://github.com/user-attachments/assets/ed7854b3-3f61-4f0f-ba27-ed19df1c1d14" />

This addresses issue [#8188](https://github.com/snipe/snipe-it/issues/8118)

Linked issue #8188: Import not displaying in Production
My import page is only showing the header - view source is showing that the input button & progress bar are in there, they are just not rendering - nor is the current file/progress table. 

Input appears to function correctly when in debug mode.

-----


#### Please confirm you have done the following before posting your bug report:

- [x] I have enabled debug mode 
- [X] I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues)

-----
#### Provide answers to these questions:

- Is this a fresh install or an upgrade? Fresh Install 
- Version of Snipe-IT you're running -  **Version v4.9.3 - build 4395 (master)**
- Version of PHP you're running - **7.4.3**
- Version of MySQL/MariaDB you're running - **10.3.22**
- What OS and web server you're running Snipe-IT on - **Ubunto 20.04 LTS**
- What method you used to install Snipe-IT (install.sh, manual installation, docker, etc) - **manual install from git**
- WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error - **no error.**
- What specific Snipe-IT page you're on, and what specific element you're interacting with to trigger the error - **/import**
- If a stacktrace is provided in the error, include that too. - **no error**
- Any errors that appear in your browser's error console. - **no errors**
- Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo. - **no**
- Include any additional information you can find in `storage/logs` and your webserver's logs. - **No errors**
- Include what you've done so far in the installation, and if you got any error messages along the way. - **customised the resources/views/hardware/labels.blade.php - otherwise vanilla**
- Indicate whether or not you've manually edited any data directly in the database - **No**

Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you.

https://snipe-it.readme.io/docs/getting-help

Linked issue comments: user: login: stale[bot]
created_at: 2020-08-29T09:13:22Z
body: Is this still relevant? We haven't heard from anyone in a bit. If so, please comment with any updates or additional detail.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Don't take it personally, we just need to keep a handle on things. Thank you for your contributions!

author_association: NONE
user: login: stale[bot]
created_at: 2020-09-05T19:54:59Z
body: This issue has been automatically closed because it has not had recent activity. If you believe this is still an issue, please confirm that this issue is still happening in the most recent version of Snipe-IT and reply to this thread to re-open it.

author_association: NONE

Diff:
diff --git a/app/Models/User.php b/app/Models/User.php
index 166e39c7852d..a686f14b9cb6 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -622,6 +622,8 @@ public static function generateFormattedNameFromFullName($users_name, $format =
                 $username = str_slug($first_name).'_'.str_slug($last_name);
             } elseif ($format == 'firstname') {
                 $username = str_slug($first_name);
+            } elseif ($format == 'lastname') {
+                $username = str_slug($last_name);
             } elseif ($format == 'firstinitial.lastname') {
                 $username = str_slug(substr($first_name, 0, 1).'.'.str_slug($last_name));
             } elseif ($format == 'lastname_firstinitial') {
diff --git a/resources/lang/en-US/admin/settings/general.php b/resources/lang/en-US/admin/settings/general.php
index c345952386a7..ffe20383ae07 100644
--- a/resources/lang/en-US/admin/settings/general.php
+++ b/resources/lang/en-US/admin/settings/general.php
@@ -396,17 +396,38 @@
     '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.',
     'text' => 'Text',
-    'firstname_lastname_format'	=> 'First Name Last Name (jane.smith)',
-    'first_name_format'	        => 'First Name (jane)',
-    'filastname_format'			=> 'First Initial Last Name (jsmith)',
-    'lastnamefirstinitial_format' =>  'Last Name First Initial (smithj)',
-    'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith)',
-    'firstinitial.lastname' => 'First Initial Last Name (j.smith)',
-    'lastname_firstinitial' => 'Last Name First Initial (smith_j)',
-    'lastname_dot_firstinitial_format' => 'Last Name First Initial (smith.j)',
-    'firstnamelastname'     => 'First Name Last Name (janesmith)',
-    'firstnamelastinitial'  => 'First Name Last Initial (janes)',
-    'lastnamefirstname'      => 'Last Name.First Name (smith.jane)',
+
+    'username_formats' => [
+        'username_format'		=> 'Username Format',
+        'firstname_lastname_format'	=> 'First Name Last Name (jane.smith)',
+        'first_name_format'	        => 'First Name (jane)',
+        'last_name_format'		=> 'Last Name (doe)',
+        'filastname_format'			=> 'First Initial Last Name (jsmith)',
+        'lastnamefirstinitial_format' =>  'Last Name First Initial (smithj)',
+        'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith)',
+        'firstinitial.lastname' => 'First Initial Last Name (j.smith)',
+        'lastname_firstinitial' => 'Last Name First Initial (smith_j)',
+        'lastname_dot_firstinitial_format' => 'Last Name First Initial (smith.j)',
+        'firstnamelastname'     => 'First Name Last Name (janesmith)',
+        'firstnamelastinitial'  => 'First Name Last Initial (janes)',
+        'lastnamefirstname'      => 'Last Name.First Name (smith.jane)',
+    ],
+
+    'email_formats' => [
+        'email_format'			=> 'Email Format',
+        'firstname_lastname_format'	=> 'First Name Last Name (jane.smith@example.com)',
+        'first_name_format'		=> 'First Name (jane@example.com)',
+        'last_name_format'		=> 'Last Name (doe@example.com)',
+        'filastname_format'			=> 'First Initial Last Name (jsmith@example.com)',
+        'lastnamefirstinitial_format' =>  'Last Name First Initial (smithj@example.com)',
+        'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith@example.com)',
+        'firstinitial.lastname' => 'First Initial Last Name (j.smith@example.com)',
+        'lastname_firstinitial' => 'Last Name First Initial (smith_j@example.com)',
+        'lastname_dot_firstinitial_format' => 'Last Name First Initial (smith.j@example.com)',
+        'firstnamelastname'     => 'First Name Last Name (janesmith@example.com)',
+        'firstnamelastinitial'  => 'First Name Last Initial (janes@example.com)',
+        'lastnamefirstname'      => 'Last Name.First Name (smith.jane@example.com)',
+    ],
 
 
 
diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php
index 72d577ff6107..bb73d94abb94 100644
--- a/resources/lang/en-US/general.php
+++ b/resources/lang/en-US/general.php
@@ -121,30 +121,13 @@
     'editprofile'  			=> 'Edit Your Profile',
     'eol'					=> 'EOL',
     'email_domain'			=> 'Email Domain',
-    'email_format'			=> 'Email Format',
     'employee_number'       => 'Employee Number',
     'email_domain_help'		=> 'This is used to generate email addresses when importing',
     'error'				    => 'Error',
     'exclude_archived'      => 'Exclude Archived Assets',
     'exclude_deleted'       => 'Exclude Deleted Assets',
     'example'				=> 'Example: ',
-    'filastname_format'			=> 'First Initial Last Name (jsmith@example.com)',
-    'firstname_lastname_format'	=> 'First Name Last Name (jane.smith@example.com)',
-    'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith@example.com)',
-    'lastnamefirstinitial_format' =>  'Last Name First Initial (smithj@example.com)',
-    'firstintial_dot_lastname_format' => 'First Initial Last Name (j.smith@example.com)',
-    'lastname_dot_firstinitial_format' => 'Last Name First Initial (smith.j@example.com)',
-    'firstname_lastname_display'	=> 'First Name Last Name (Jane Smith)',
-    'lastname_firstname_display'	=> 'Last Name First Name (Smith Jane)',
-    'name_display_format'	=> 'Name Display Format',
-    'first'					=> 'First',
-    'firstnamelastname'     => 'First Name Last Name (janesmith@example.com)',
-    'lastname_firstinitial' => 'Last Name First Initial (smith_j@example.com)',
-    'firstinitial.lastname' => 'First Initial Last Name (j.smith@example.com)',
-    'firstnamelastinitial'  => 'First Name Last Initial (janes@example.com)',
-    'lastnamefirstname'      => 'Last Name.First Name (smith.jane@example.com)',
-    'first_name'			=> 'First Name',
-    'first_name_format'		=> 'First Name (jane@example.com)',
+
     'files'                 => 'Files',
     'file_name'				=> 'File',
     'file_type'             => 'File Type',
@@ -186,6 +169,11 @@
     'last'					=> 'Last',
     'last_login'            => 'Last Login',
     'last_name'             => 'Last Name',
+    'firstname_lastname_display'	=> 'First Name Last Name (Jane Smith)',
+    'lastname_firstname_display'	=> 'Last Name First Name (Smith Jane)',
+    'name_display_format'	=> 'Name Display Format',
+    'first'					=> 'First',
+    'first_name'			=> 'First Name',
     'license'				=> 'License',
     'license_report'        => 'License Report',
     'licenses_available'	=> 'Licenses available',
@@ -314,7 +302,6 @@
     'type'  				=> 'Type',
     'undeployable'			=> 'Un-deployable',
     'unknown_admin'			=> 'Unknown Admin',
-    'username_format'		=> 'Username Format',
     'username'              => 'Username',
     'update'                => 'Update',
     'updating_item' => 'Updating :item',
diff --git a/resources/macros/macros.php b/resources/macros/macros.php
index aa55ba8fd86a..9c0227670756 100644
--- a/resources/macros/macros.php
+++ b/resources/macros/macros.php
@@ -191,17 +191,18 @@
 
 Form::macro('email_format', function ($name = 'email_format', $selected = null, $class = null) {
     $formats = [
-        'firstname.lastname' => trans('general.firstname_lastname_format'),
-        'firstname' => trans('general.first_name_format'),
-        'filastname' => trans('general.filastname_format'),
-        'lastnamefirstinitial' => trans('general.lastnamefirstinitial_format'),
-        'firstname_lastname' => trans('general.firstname_lastname_underscore_format'),
-        'firstinitial.lastname' => trans('general.firstinitial.lastname'),
-        'lastname_firstinitial' => trans('general.lastname_firstinitial'),
-        'lastname.firstinitial' => trans('general.lastname_dot_firstinitial_format'),
-        'firstnamelastname' => trans('general.firstnamelastname'),
-        'firstnamelastinitial' => trans('general.firstnamelastinitial'),
-        'lastname.firstname' => trans('general.lastnamefirstname'),
+        'firstname.lastname' => trans('admin/settings/general.email_formats.firstname_lastname_format'),
+        'firstname' => trans('admin/settings/general.email_formats.first_name_format'),
+        'lastname' => trans('admin/settings/general.email_formats.last_name_format'),
+        'filastname' => trans('admin/settings/general.email_formats.filastname_format'),
+        'lastnamefirstinitial' => trans('admin/settings/general.email_formats.lastnamefirstinitial_format'),
+        'firstname_lastname' => trans('admin/settings/general.email_formats.firstname_lastname_underscore_format'),
+        'firstinitial.lastname' => trans('admin/settings/general.email_formats.firstinitial.lastname'),
+        'lastname_firstinitial' => trans('admin/settings/general.email_formats.lastname_firstinitial'),
+        'lastname.firstinitial' => trans('admin/settings/general.email_formats.lastname_dot_firstinitial_format'),
+        'firstnamelastname' => trans('admin/settings/general.email_formats.firstnamelastname'),
+        'firstnamelastinitial' => trans('admin/settings/general.email_formats.firstnamelastinitial'),
+        'lastname.firstname' => trans('admin/settings/general.email_formats.lastnamefirstname'),
     ];
 
     $select = '<select name="'.$name.'" class="'.$class.'" style="width: 100%" aria-label="'.$name.'">';
@@ -216,17 +217,18 @@
 
 Form::macro('username_format', function ($name = 'username_format', $selected = null, $class = null) {
     $formats = [
-        'firstname.lastname' => trans('admin/settings/general.firstname_lastname_format'),
-        'firstname' => trans('admin/settings/general.first_name_format'),
-        'filastname' => trans('admin/settings/general.filastname_format'),
-        'lastnamefirstinitial' => trans('admin/settings/general.lastnamefirstinitial_format'),
-        'firstname_lastname' => trans('admin/settings/general.firstname_lastname_underscore_format'),
-        'firstinitial.lastname' => trans('admin/settings/general.firstinitial.lastname'),
-        'lastname_firstinitial' => trans('admin/settings/general.lastname_firstinitial'),
-        'lastname.firstinitial' => trans('admin/settings/general.lastname_dot_firstinitial_format'),
-        'firstnamelastname' => trans('admin/settings/general.firstnamelastname'),
-        'firstnamelastinitial' => trans('admin/settings/general.firstnamelastinitial'),
-        'lastname.firstname' => trans('admin/settings/general.lastnamefirstname'),
+        'firstname.lastname' => trans('admin/settings/general.username_formats.firstname_lastname_format'),
+        'firstname' => trans('admin/settings/general.username_formats.first_name_format'),
+        'lastname' => trans('admin/settings/general.username_formats.last_name_format'),
+        'filastname' => trans('admin/settings/general.username_formats.filastname_format'),
+        'lastnamefirstinitial' => trans('admin/settings/general.username_formats.lastnamefirstinitial_format'),
+        'firstname_lastname' => trans('admin/settings/general.username_formats.firstname_lastname_underscore_format'),
+        'firstinitial.lastname' => trans('admin/settings/general.username_formats.firstinitial.lastname'),
+        'lastname_firstinitial' => trans('admin/settings/general.username_formats.lastname_firstinitial'),
+        'lastname.firstinitial' => trans('admin/settings/general.username_formats.lastname_dot_firstinitial_format'),
+        'firstnamelastname' => trans('admin/settings/general.username_formats.firstnamelastname'),
+        'firstnamelastinitial' => trans('admin/settings/general.username_formats.firstnamelastinitial'),
+        'lastname.firstname' => trans('admin/settings/general.username_formats.lastnamefirstname'),
     ];
 
     $select = '<select name="'.$name.'" class="'.$class.'" style="width: 100%" aria-label="'.$name.'">';
diff --git a/resources/views/settings/general.blade.php b/resources/views/settings/general.blade.php
index 481fa90921ca..b4fa3cc5c31a 100644
--- a/resources/views/settings/general.blade.php
+++ b/resources/views/settings/general.blade.php
@@ -107,7 +107,7 @@
                     <!-- Email format -->
                     <div class="form-group {{ $errors->has('email_format') ? 'error' : '' }}">
                         <div class="col-md-3">
-                            <label for="email_format">{{ trans('general.email_format') }}</label>
+                            <label for="email_format">{{ trans('admin/settings/general.email_formats.email_format') }}</label>
                         </div>
                         <div class="col-md-9">
                             {!! Form::email_format('email_format', old('email_format', $setting->email_format), 'select2') !!}
@@ -118,7 +118,7 @@
                     <!-- Username format -->
                     <div class="form-group {{ $errors->has('username_format') ? 'error' : '' }}">
                         <div class="col-md-3">
-                            <label for="username_format">{{ trans('general.username_format') }}</label>
+                            <label for="username_format">{{ trans('admin/settings/general.username_formats.username_format') }}</label>
                         </div>
                         <div class="col-md-9">
                             {!! Form::username_format('username_format', old('username_format', $setting->username_format), 'select2') !!}
diff --git a/tests/Unit/UserTest.php b/tests/Unit/UserTest.php
index 9554085c0d7e..f54969a5da24 100644
--- a/tests/Unit/UserTest.php
+++ b/tests/Unit/UserTest.php
@@ -32,6 +32,22 @@ public function testFirstNameEmail()
         $this->assertEquals($expected_email, $user['username'] . '@example.com');
     }
 
+    public function testLastName()
+    {
+        $fullname = "Natalia Allanovna Romanova-O'Shostakova";
+        $expected_username = 'allanovna-romanova-oshostakova';
+        $user = User::generateFormattedNameFromFullName($fullname, 'lastname');
+        $this->assertEquals($expected_username, $user['username']);
+    }
+
+    public function testLastNameEmail()
+    {
+        $fullname = "Natalia Allanovna Romanova-O'Shostakova";
+        $expected_username = 'allanovna-romanova-oshostakova@example.com';
+        $user = User::generateFormattedNameFromFullName($fullname, 'lastname');
+        $this->assertEquals($expected_username, $user['username'] . '@example.com');
+    }
+
     public function testFirstNameDotLastName()
     {
         $fullname = "Natalia Allanovna Romanova-O'Shostakova";


Reviews:
user: login: snipe
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T18:49:35Z
user: login: snipe
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T00:05:31Z
user: login: akemidx
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T19:28:28Z

Review comments:
diff_hunk: @@ -186,6 +186,7 @@
     'last'					=> 'Last',
     'last_login'            => 'Last Login',
     'last_name'             => 'Last Name',
+    'last_name_format'		=> 'Last Name (doe@example.com)',
path: resources/lang/en-US/general.php
user: login: snipe
body: I feel like having these in two places is kinda confusing. I'd probably opt to make a new array in the `resources/lang/en-US/admin/settings/general.php` with format translations.
created_at: 2025-04-08T18:49:35Z
author_association: MEMBER
diff_hunk: @@ -186,6 +186,7 @@
     'last'					=> 'Last',
     'last_login'            => 'Last Login',
     'last_name'             => 'Last Name',
+    'last_name_format'		=> 'Last Name (doe@example.com)',
path: resources/lang/en-US/general.php
user: login: snipe
body: Something like (in `admin/settings/general.php`):

```php
    'email_formats' => [
        'last_name_format' => 'Last Name (doe@example.com)',
        'first_name_format' => 'First Name (jane@example.com)',
	... etc
    ],
```

and 

```php
    'username_formats' => [
        'last_name_format' => 'Last Name (doe)',
        'first_name_format' => 'First Name (jane)',
	... etc
    ],
```

just so that they're visually closer to each other, since they basically do the same general thing.
created_at: 2025-04-09T00:05:31Z
author_association: MEMBER
diff_hunk: @@ -186,6 +186,7 @@
     'last'					=> 'Last',
     'last_login'            => 'Last Login',
     'last_name'             => 'Last Name',
+    'last_name_format'		=> 'Last Name (doe@example.com)',
path: resources/lang/en-US/general.php
user: login: akemidx
body: yea, that would be better. alrighty.
created_at: 2025-04-09T19:28:28Z
author_association: CONTRIBUTOR

PR comments:
user: login: what-the-diff[bot]
created_at: 2025-04-04T03:40:53Z
body: ## PR Summary

* **Enhancements to User Profile**
Updated the system to now create a username using the user's last name as well.

* **Language Support Improvements**
Added a new translation element to support display of 'last name format' in different languages.

* **Adjustments to Macros**
Included a new option in the set of available macros that specifically caters to last name formatting.

* **Verification through Testing**
Implemented a new test case to ensure that the feature of username generation based on user's last name is working as expected.


author_association: NONE
user: login: snipe
created_at: 2025-04-08T05:33:34Z
body: Can you resolve these conflicts?
author_association: MEMBER
user: login: snipe
created_at: 2025-04-09T20:27:07Z
body: Looks good!
author_association: MEMBER