feat: Employee HR management – .NET Core Web API backend (Phase 1)#1
Draft
Copilot wants to merge 6 commits into
Draft
feat: Employee HR management – .NET Core Web API backend (Phase 1)#1Copilot wants to merge 6 commits into
Copilot wants to merge 6 commits into
Conversation
- Add Employee entity, SourceType enum, HumanResourcesAdminSourceTypes role - Add SearchEmployees query with role-aware filtering and at-least-one-criterion validation - Add GetNextRegistrationNumber query for UI hint - Add GetEmployeeLookups query (role-filtered source types + active/passive codes) - Add CreateEmployee command with business rules and race-condition-safe registration number generation - Add UpdateEmployee command with full update validations - Add IEmployeeRegistrationNumberService interface with atomic GenerateNextAsync - Add EmployeeRegistrationNumberService using DB transaction to prevent race conditions - Add Employees minimal API endpoint group with all 7 endpoints - Seed HumanResourcesAdminSourceTypes role in DB initializer - Pre-existing NU1903 vulnerability unrelated to this feature Agent-Logs-Url: https://github.com/defactoAdil/CleanArchitecture/sessions/5820a821-591e-44e7-ad12-0141efb2e2f4 Co-authored-by: defactoAdil <189980729+defactoAdil@users.noreply.github.com>
- Fix transaction race condition: generate+insert now wrapped in single BeginTransactionAsync call - Add BeginTransactionAsync to IApplicationDbContext and ApplicationDbContext - Extract shared ActivePassiveCodes.Normalize helper to eliminate duplication - Fix duplicate lookup endpoints to return specific data (SourceTypes vs ActivePassiveCodes) - Simplify EmployeeRegistrationNumberService (transaction managed by caller) Agent-Logs-Url: https://github.com/defactoAdil/CleanArchitecture/sessions/5820a821-591e-44e7-ad12-0141efb2e2f4 Co-authored-by: defactoAdil <189980729+defactoAdil@users.noreply.github.com>
Copilot created this pull request from a session on behalf of
defactoAdil
April 17, 2026 14:07
View session
Owner
|
yapay zekaya analiz verip kod yazdırma testi yaptım. |
There was a problem hiding this comment.
Pull request overview
Implements Phase 1 of the Employee HR management backend for the migrated EmployeeList screen, adding the Employee domain model, CQRS/MediatR handlers (search/create/update/lookups/registration number preview), EF Core persistence, and minimal API endpoints under the existing Clean Architecture structure.
Changes:
- Added
Employeedomain entity +SourceTypeenum + EF Core configuration and DbContext wiring. - Implemented Employee CQRS handlers/validators, lookup queries, and registration-number generation service.
- Added minimal API endpoints for employee search/read/create/update and lookups; seeded the new HR admin role.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Web/Endpoints/Employees.cs | Adds minimal API endpoints for employee operations and lookups. |
| src/Infrastructure/Services/EmployeeRegistrationNumberService.cs | Implements MAX+1 registration number generation/preview per source type. |
| src/Infrastructure/DependencyInjection.cs | Registers IEmployeeRegistrationNumberService. |
| src/Infrastructure/Data/Configurations/EmployeeConfiguration.cs | Adds EF mapping and constraints (unique index, max lengths, required fields). |
| src/Infrastructure/Data/ApplicationDbContextInitialiser.cs | Seeds the new HumanResourcesAdminSourceTypes role. |
| src/Infrastructure/Data/ApplicationDbContext.cs | Adds DbSet<Employee> and exposes BeginTransactionAsync. |
| src/Domain/Enums/SourceType.cs | Defines source type enum values. |
| src/Domain/Entities/Employee.cs | Introduces Employee entity fields (auditable). |
| src/Domain/Constants/Roles.cs | Adds HumanResourcesAdminSourceTypes role constant. |
| src/Application/Employees/Queries/SearchEmployees/SearchEmployees.cs | Adds employee search query, validator, and handler with role-based filtering. |
| src/Application/Employees/Queries/SearchEmployees/EmployeeSearchRequest.cs | Defines search request contract for filtering. |
| src/Application/Employees/Queries/SearchEmployees/EmployeeDto.cs | Adds DTO + AutoMapper mapping with derived fields. |
| src/Application/Employees/Queries/GetNextRegistrationNumber/GetNextRegistrationNumber.cs | Adds “peek” query for next registration number. |
| src/Application/Employees/Queries/GetEmployeeLookups/GetEmployeeLookups.cs | Adds role-filtered lookups for source types and active/passive codes. |
| src/Application/Employees/Queries/GetEmployeeLookups/EmployeeLookupsVm.cs | Defines lookup VM and DTOs. |
| src/Application/Employees/Common/ActivePassiveCodes.cs | Adds shared normalization helper for active/passive codes. |
| src/Application/Employees/Commands/UpdateEmployee/UpdateEmployeeCommandValidator.cs | Adds update validation rules, including source type restrictions. |
| src/Application/Employees/Commands/UpdateEmployee/UpdateEmployee.cs | Adds update command + handler with role-based edit restrictions. |
| src/Application/Employees/Commands/CreateEmployee/CreateEmployeeCommandValidator.cs | Adds create validation rules (incl. restricted source types). |
| src/Application/Employees/Commands/CreateEmployee/CreateEmployee.cs | Adds create command + handler with transactional registration number generation. |
| src/Application/Common/Interfaces/IEmployeeRegistrationNumberService.cs | Introduces interface for registration number generation/peek. |
| src/Application/Common/Interfaces/IApplicationDbContext.cs | Extends app DbContext interface with Employees and BeginTransactionAsync. |
Comments suppressed due to low confidence (1)
src/Infrastructure/Data/ApplicationDbContextInitialiser.cs:94
- The new
HumanResourcesAdminSourceTypesrole is created during seeding, but the default seededadministrator@localhostuser is only added to theAdministratorrole. Since the new employee feature’s “admin” checks are based onHumanResourcesAdminSourceTypes, the seeded admin user won’t be able to access/administer SAP/OzonTekstil behaviors in dev/test environments. Consider adding the seeded admin user to the new role as well (or aligning the checks to includeAdministrator).
var administratorRole = new IdentityRole(Roles.Administrator);
var hrAdminRole = new IdentityRole(Roles.HumanResourcesAdminSourceTypes);
if (_roleManager.Roles.All(r => r.Name != administratorRole.Name))
{
await _roleManager.CreateAsync(administratorRole);
}
if (_roleManager.Roles.All(r => r.Name != hrAdminRole.Name))
{
await _roleManager.CreateAsync(hrAdminRole);
}
// Default users
var administrator = new ApplicationUser { UserName = "administrator@localhost", Email = "administrator@localhost" };
if (_userManager.Users.All(u => u.UserName != administrator.UserName))
{
await _userManager.CreateAsync(administrator, "Administrator1!");
if (!string.IsNullOrWhiteSpace(administratorRole.Name))
{
await _userManager.AddToRolesAsync(administrator, new [] { administratorRole.Name });
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+35
to
+38
| RuleFor(c => c.SourceTypeStr) | ||
| .Must(st => !RestrictedSourceTypes.Contains(st)) | ||
| .WithMessage("SAP and OzonTekstil source types cannot be selected when creating an employee.") | ||
| .When(c => !string.IsNullOrWhiteSpace(c.SourceTypeStr)); |
Comment on lines
+50
to
+68
| public async Task<string> Handle(CreateEmployeeCommand request, CancellationToken cancellationToken) | ||
| { | ||
| // Resolve default source type based on role | ||
| var sourceType = string.IsNullOrWhiteSpace(request.SourceTypeStr) | ||
| ? DefaultSourceType() | ||
| : request.SourceTypeStr; | ||
|
|
||
| // BusinessUnitId is only set for admin users | ||
| var isAdmin = _user.Roles?.Contains(Roles.HumanResourcesAdminSourceTypes) ?? false; | ||
| var businessUnitId = isAdmin ? request.BusinessUnitId : null; | ||
|
|
||
| // Wrap number generation + INSERT in one transaction to prevent race conditions. | ||
| // The unique index on RegistrationNumber is the final safety net. | ||
| await using var transaction = await _context.BeginTransactionAsync(cancellationToken); | ||
| try | ||
| { | ||
| var registrationNumber = await _registrationNumberService | ||
| .GenerateNextAsync(sourceType, cancellationToken); | ||
|
|
Comment on lines
+16
to
+21
| groupBuilder.MapPost(SearchEmployees); | ||
| groupBuilder.MapGet(GetEmployee, "{registrationNumber}"); | ||
| groupBuilder.MapGet(GetNextRegistrationNumber, "next-registration-number/{sourceType}"); | ||
| groupBuilder.MapPost(CreateEmployee, string.Empty); | ||
| groupBuilder.MapPut(UpdateEmployee, "{registrationNumber}"); | ||
| groupBuilder.MapGet(GetSourceTypeLookups, "lookup/source-types"); |
| ISender sender, CreateEmployeeCommand command) | ||
| { | ||
| var registrationNumber = await sender.Send(command); | ||
| return TypedResults.Created($"/api/employees/{registrationNumber}", registrationNumber); |
| if (!string.IsNullOrWhiteSpace(r.FirstName)) return true; | ||
| if (!string.IsNullOrWhiteSpace(r.LastName)) return true; | ||
| if (r.SourceTypeList is { Count: > 0 }) return true; | ||
| if (r.IsTerminated.HasValue && !string.IsNullOrWhiteSpace(r.ActivePassiveCode)) return true; |
Comment on lines
+34
to
+36
| .NotEmpty() | ||
| .Length(12).WithMessage("Personal mobile number must be exactly 12 characters.") | ||
| .Must(n => n!.StartsWith("90")).WithMessage("Personal mobile number must start with '90'."); |
Comment on lines
+61
to
+94
| // Wrap number generation + INSERT in one transaction to prevent race conditions. | ||
| // The unique index on RegistrationNumber is the final safety net. | ||
| await using var transaction = await _context.BeginTransactionAsync(cancellationToken); | ||
| try | ||
| { | ||
| var registrationNumber = await _registrationNumberService | ||
| .GenerateNextAsync(sourceType, cancellationToken); | ||
|
|
||
| var employee = new Employee | ||
| { | ||
| RegistrationNumber = registrationNumber, | ||
| IdentityNumber = request.IdentityNumber, | ||
| Firstname = request.Firstname, | ||
| Lastname = request.Lastname, | ||
| PersonalMobileNumber = request.PersonalMobileNumber, | ||
| SourceTypeStr = sourceType, | ||
| ActivePassiveCode = ActivePassiveCodes.Normalize(request.ActivePassiveCode), | ||
| IsTerminated = request.IsTerminated, | ||
| CompanyName = request.CompanyName, | ||
| BusinessUnitId = businessUnitId, | ||
| Description = request.Description | ||
| }; | ||
|
|
||
| _context.Employees.Add(employee); | ||
| await _context.SaveChangesAsync(cancellationToken); | ||
| await transaction.CommitAsync(cancellationToken); | ||
|
|
||
| return registrationNumber; | ||
| } | ||
| catch | ||
| { | ||
| await transaction.RollbackAsync(cancellationToken); | ||
| throw; | ||
| } |
Comment on lines
+90
to
+91
| if (allowedSourceTypes.Count > 0) | ||
| query = query.Where(e => allowedSourceTypes.Contains(e.SourceTypeStr)); |
- Fix backend route conflict: SearchEmployees → POST /api/Employees/search (was POST /api/Employees, conflicting with CreateEmployee) - Regenerate TypeScript API client (searchEmployees method now generated) - Add EmployeesComponent with search form, results table and create/edit dialog - Role-based UX: admin detection via source-type lookup (SAP/OzonTekstil visible ↔ isAdmin) - BusinessUnitId field shown only for admin users - canEdit flag respected: Edit button shown only when employee is editable - Registration number auto-populated from API on source type change (create only) - Source type multi-select chip UI - Active/Passive badge styling in results table - Register route /employees with AuthGuard in app.module.ts - Add Employees nav link in nav-menu - Add employee CSS classes to global styles.scss Agent-Logs-Url: https://github.com/defactoAdil/CleanArchitecture/sessions/8ad11855-a989-4523-be83-b5a1202b1270 Co-authored-by: defactoAdil <189980729+defactoAdil@users.noreply.github.com>
…ator Mobile number is nullable on the domain entity — `.NotEmpty()` incorrectly prevented blank values on update. Now applies length/prefix rules only when the field is provided, matching CreateEmployeeCommandValidator behaviour. Agent-Logs-Url: https://github.com/defactoAdil/CleanArchitecture/sessions/8ad11855-a989-4523-be83-b5a1202b1270 Co-authored-by: defactoAdil <189980729+defactoAdil@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
defactoAdil
approved these changes
Apr 23, 2026
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
employees/employees.component.tswith signals, search, create/edit logic, role awarenessemployees/employees.component.html(search form with multi-select chips, results table, create/edit dialog)styles.scssapp.module.tswith route/employeesandAuthGuardnav-menu.component.html